The current version of raw-posix always uses ioctl(FS_IOC_FIEMAP) if FIEMAP is available; lseek with SEEK_HOLE/SEEK_DATA are not even compiled in in this case. However, there may be implementations which support the latter but not the former (e.g., NFSv4.2). In this case, raw-posix should fall back to lseek with SEEK_HOLE/SEEK_DATA if FIEMAP does not work.
Signed-off-by: Max Reitz <mre...@redhat.com> --- As Linux does not yet implement NFSv4.2 (I don't even know whether the specification is complete), I have no way of testing whether this actually works for the proposed case. But as it doesn't break any of the existing test cases, it should be fine. --- block/raw-posix.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/block/raw-posix.c b/block/raw-posix.c index 3ce026d..e523633 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -146,6 +146,9 @@ typedef struct BDRVRawState { bool has_discard:1; bool has_write_zeroes:1; bool discard_zeroes:1; +#if defined CONFIG_FIEMAP && defined SEEK_HOLE && defined SEEK_DATA + bool use_seek_hole_data; +#endif } BDRVRawState; typedef struct BDRVRawReopenState { @@ -1291,6 +1294,7 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { + BDRVRawState *s = bs->opaque; off_t start, data, hole; int64_t ret; @@ -1303,22 +1307,31 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; #ifdef CONFIG_FIEMAP - - BDRVRawState *s = bs->opaque; struct { struct fiemap fm; struct fiemap_extent fe; } f; +#if defined SEEK_HOLE && defined SEEK_DATA + if (s->use_seek_hole_data) { + goto try_seek_hole_data; + } +#endif + f.fm.fm_start = start; f.fm.fm_length = (int64_t)nb_sectors * BDRV_SECTOR_SIZE; f.fm.fm_flags = 0; f.fm.fm_extent_count = 1; f.fm.fm_reserved = 0; if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) { +#if defined SEEK_HOLE && defined SEEK_DATA + s->use_seek_hole_data = true; + goto try_seek_hole_data; +#else /* Assume everything is allocated. */ *pnum = nb_sectors; return ret; +#endif } if (f.fm.fm_mapped_extents == 0) { @@ -1336,10 +1349,11 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, } } -#elif defined SEEK_HOLE && defined SEEK_DATA - - BDRVRawState *s = bs->opaque; + goto done; +#endif +#if defined SEEK_HOLE && defined SEEK_DATA +try_seek_hole_data: hole = lseek(s->fd, start, SEEK_HOLE); if (hole == -1) { /* -ENXIO indicates that sector_num was past the end of the file. @@ -1360,11 +1374,12 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, data = lseek(s->fd, 0, SEEK_END); } } -#else +#elif !defined CONFIG_FIEMAP data = 0; hole = start + nb_sectors * BDRV_SECTOR_SIZE; #endif +done: if (data <= start) { /* On a data extent, compute sectors to the end of the extent. */ *pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE); -- 1.9.2