diff --git a/block/raw-posix.c b/block/raw-posix.c index 3ce026db58..6586a0c9e1 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; +#ifdef CONFIG_FIEMAP + bool skip_fiemap; +#endif } BDRVRawState; typedef struct BDRVRawReopenState { @@ -1272,6 +1275,83 @@ static int raw_create(const char *filename, QEMUOptionParameter *options, return result; } +static int64_t try_fiemap(BlockDriverState *bs, off_t start, off_t *data, + off_t *hole, int nb_sectors, int *pnum) +{ +#ifdef CONFIG_FIEMAP + BDRVRawState *s = bs->opaque; + int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + struct { + struct fiemap fm; + struct fiemap_extent fe; + } f; + + if (s->skip_fiemap) { + return -ENOTSUP; + } + + 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) { + s->skip_fiemap = true; + return -errno; + } + + if (f.fm.fm_mapped_extents == 0) { + /* No extents found, data is beyond f.fm.fm_start + f.fm.fm_length. + * f.fm.fm_start + f.fm.fm_length must be clamped to the file size! + */ + off_t length = lseek(s->fd, 0, SEEK_END); + *hole = f.fm.fm_start; + *data = MIN(f.fm.fm_start + f.fm.fm_length, length); + } else { + *data = f.fe.fe_logical; + *hole = f.fe.fe_logical + f.fe.fe_length; + if (f.fe.fe_flags & FIEMAP_EXTENT_UNWRITTEN) { + ret |= BDRV_BLOCK_ZERO; + } + } + + return ret; +#else + return -ENOTSUP; +#endif +} + +static int64_t try_seek_hole(BlockDriverState *bs, off_t start, off_t *data, + off_t *hole, int *pnum) +{ +#if defined SEEK_HOLE && defined SEEK_DATA + BDRVRawState *s = bs->opaque; + + *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; + } + + if (*hole > start) { + *data = start; + } else { + /* On a hole. We need another syscall to find its end. */ + *data = lseek(s->fd, start, SEEK_DATA); + if (*data == -1) { + *data = lseek(s->fd, 0, SEEK_END); + } + } + + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; +#else + return -ENOTSUP; +#endif +} + /* * Returns true iff the specified sector is present in the disk image. Drivers * not implementing the functionality are assumed to not support backing files, @@ -1288,10 +1368,10 @@ static int raw_create(const char *filename, QEMUOptionParameter *options, * beyond the end of the disk image it will be clamped. */ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, - int64_t sector_num, - int nb_sectors, int *pnum) + int64_t sector_num, + int nb_sectors, int *pnum) { - off_t start, data, hole; + off_t start, data = 0, hole = 0; int64_t ret; ret = fd_open(bs); @@ -1300,71 +1380,18 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, } start = sector_num * BDRV_SECTOR_SIZE; - 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; - - 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) { - /* Assume everything is allocated. */ - *pnum = nb_sectors; - return ret; - } - - if (f.fm.fm_mapped_extents == 0) { - /* No extents found, data is beyond f.fm.fm_start + f.fm.fm_length. - * f.fm.fm_start + f.fm.fm_length must be clamped to the file size! - */ - off_t length = lseek(s->fd, 0, SEEK_END); - hole = f.fm.fm_start; - data = MIN(f.fm.fm_start + f.fm.fm_length, length); - } else { - data = f.fe.fe_logical; - hole = f.fe.fe_logical + f.fe.fe_length; - if (f.fe.fe_flags & FIEMAP_EXTENT_UNWRITTEN) { - ret |= BDRV_BLOCK_ZERO; + ret = try_fiemap(bs, start, &data, &hole, nb_sectors, pnum); + if (ret < 0) { + ret = try_seek_hole(bs, start, &data, &hole, pnum); + if (ret < 0) { + /* Assume everything is allocated. */ + data = 0; + hole = start + nb_sectors * BDRV_SECTOR_SIZE; + ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; } } -#elif defined SEEK_HOLE && defined SEEK_DATA - - BDRVRawState *s = bs->opaque; - - 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); - - /* Most likely EINVAL. Assume everything is allocated. */ - *pnum = nb_sectors; - return ret; - } - - if (hole > start) { - data = start; - } else { - /* On a hole. We need another syscall to find its end. */ - data = lseek(s->fd, start, SEEK_DATA); - if (data == -1) { - data = lseek(s->fd, 0, SEEK_END); - } - } -#else - data = 0; - hole = start + nb_sectors * BDRV_SECTOR_SIZE; -#endif - if (data <= start) { /* On a data extent, compute sectors to the end of the extent. */ *pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);