#include "module.h"
#include "block/raw-posix-aio.h"
-#ifdef CONFIG_COCOA
+#if defined(__APPLE__) && (__MACH__)
#include <paths.h>
#include <sys/param.h>
#include <IOKit/IOKitLib.h>
#include <sys/param.h>
#include <linux/cdrom.h>
#include <linux/fd.h>
+#include <linux/fs.h>
+#endif
+#ifdef CONFIG_FIEMAP
+#include <linux/fiemap.h>
#endif
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <sys/disk.h>
}
}
+ /* We're falling back to POSIX AIO in some cases so init always */
+ if (paio_init() < 0) {
+ goto out_free_buf;
+ }
+
#ifdef CONFIG_LINUX_AIO
+ /*
+ * Currently Linux do AIO only for files opened with O_DIRECT
+ * specified so check NOCACHE flag too
+ */
if ((bdrv_flags & (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) ==
(BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) {
- /* We're falling back to POSIX AIO in some cases */
- paio_init();
-
s->aio_ctx = laio_init();
if (!s->aio_ctx) {
goto out_free_buf;
} else
#endif
{
- if (paio_init() < 0) {
- goto out_free_buf;
- }
#ifdef CONFIG_LINUX_AIO
s->use_aio = 0;
#endif
#endif
*/
-/*
- * offset and count are in bytes, but must be multiples of 512 for files
- * opened with O_DIRECT. buf must be aligned to 512 bytes then.
- *
- * This function may be called without alignment if the caller ensures
- * that O_DIRECT is not in effect.
- */
-static int raw_pread_aligned(BlockDriverState *bs, int64_t offset,
- uint8_t *buf, int count)
-{
- BDRVRawState *s = bs->opaque;
- int ret;
-
- ret = fd_open(bs);
- if (ret < 0)
- return ret;
-
- ret = pread(s->fd, buf, count, offset);
- if (ret == count)
- return ret;
-
- /* Allow reads beyond the end (needed for pwrite) */
- if ((ret == 0) && bs->growable) {
- int64_t size = raw_getlength(bs);
- if (offset >= size) {
- memset(buf, 0, count);
- return count;
- }
- }
-
- DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
- "] read failed %d : %d = %s\n",
- s->fd, bs->filename, offset, buf, count,
- bs->total_sectors, ret, errno, strerror(errno));
-
- /* Try harder for CDrom. */
- if (s->type != FTYPE_FILE) {
- ret = pread(s->fd, buf, count, offset);
- if (ret == count)
- return ret;
- ret = pread(s->fd, buf, count, offset);
- if (ret == count)
- return ret;
-
- DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
- "] retry read failed %d : %d = %s\n",
- s->fd, bs->filename, offset, buf, count,
- bs->total_sectors, ret, errno, strerror(errno));
- }
-
- return (ret < 0) ? -errno : ret;
-}
-
-/*
- * offset and count are in bytes, but must be multiples of the sector size
- * for files opened with O_DIRECT. buf must be aligned to sector size bytes
- * then.
- *
- * This function may be called without alignment if the caller ensures
- * that O_DIRECT is not in effect.
- */
-static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset,
- const uint8_t *buf, int count)
-{
- BDRVRawState *s = bs->opaque;
- int ret;
-
- ret = fd_open(bs);
- if (ret < 0)
- return -errno;
-
- ret = pwrite(s->fd, buf, count, offset);
- if (ret == count)
- return ret;
-
- DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
- "] write failed %d : %d = %s\n",
- s->fd, bs->filename, offset, buf, count,
- bs->total_sectors, ret, errno, strerror(errno));
-
- return (ret < 0) ? -errno : ret;
-}
-
-
-/*
- * offset and count are in bytes and possibly not aligned. For files opened
- * with O_DIRECT, necessary alignments are ensured before calling
- * raw_pread_aligned to do the actual read.
- */
-static int raw_pread(BlockDriverState *bs, int64_t offset,
- uint8_t *buf, int count)
-{
- BDRVRawState *s = bs->opaque;
- unsigned sector_mask = bs->buffer_alignment - 1;
- int size, ret, shift, sum;
-
- sum = 0;
-
- if (s->aligned_buf != NULL) {
-
- if (offset & sector_mask) {
- /* align offset on a sector size bytes boundary */
-
- shift = offset & sector_mask;
- size = (shift + count + sector_mask) & ~sector_mask;
- if (size > s->aligned_buf_size)
- size = s->aligned_buf_size;
- ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size);
- if (ret < 0)
- return ret;
-
- size = bs->buffer_alignment - shift;
- if (size > count)
- size = count;
- memcpy(buf, s->aligned_buf + shift, size);
-
- buf += size;
- offset += size;
- count -= size;
- sum += size;
-
- if (count == 0)
- return sum;
- }
- if (count & sector_mask || (uintptr_t) buf & sector_mask) {
-
- /* read on aligned buffer */
-
- while (count) {
-
- size = (count + sector_mask) & ~sector_mask;
- if (size > s->aligned_buf_size)
- size = s->aligned_buf_size;
-
- ret = raw_pread_aligned(bs, offset, s->aligned_buf, size);
- if (ret < 0) {
- return ret;
- } else if (ret == 0) {
- fprintf(stderr, "raw_pread: read beyond end of file\n");
- abort();
- }
-
- size = ret;
- if (size > count)
- size = count;
-
- memcpy(buf, s->aligned_buf, size);
-
- buf += size;
- offset += size;
- count -= size;
- sum += size;
- }
-
- return sum;
- }
- }
-
- return raw_pread_aligned(bs, offset, buf, count) + sum;
-}
-
-static int raw_read(BlockDriverState *bs, int64_t sector_num,
- uint8_t *buf, int nb_sectors)
-{
- int ret;
-
- ret = raw_pread(bs, sector_num * BDRV_SECTOR_SIZE, buf,
- nb_sectors * BDRV_SECTOR_SIZE);
- if (ret == (nb_sectors * BDRV_SECTOR_SIZE))
- ret = 0;
- return ret;
-}
-
-/*
- * offset and count are in bytes and possibly not aligned. For files opened
- * with O_DIRECT, necessary alignments are ensured before calling
- * raw_pwrite_aligned to do the actual write.
- */
-static int raw_pwrite(BlockDriverState *bs, int64_t offset,
- const uint8_t *buf, int count)
-{
- BDRVRawState *s = bs->opaque;
- unsigned sector_mask = bs->buffer_alignment - 1;
- int size, ret, shift, sum;
-
- sum = 0;
-
- if (s->aligned_buf != NULL) {
-
- if (offset & sector_mask) {
- /* align offset on a sector size bytes boundary */
- shift = offset & sector_mask;
- ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf,
- bs->buffer_alignment);
- if (ret < 0)
- return ret;
-
- size = bs->buffer_alignment - shift;
- if (size > count)
- size = count;
- memcpy(s->aligned_buf + shift, buf, size);
-
- ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf,
- bs->buffer_alignment);
- if (ret < 0)
- return ret;
-
- buf += size;
- offset += size;
- count -= size;
- sum += size;
-
- if (count == 0)
- return sum;
- }
- if (count & sector_mask || (uintptr_t) buf & sector_mask) {
-
- while ((size = (count & ~sector_mask)) != 0) {
-
- if (size > s->aligned_buf_size)
- size = s->aligned_buf_size;
-
- memcpy(s->aligned_buf, buf, size);
-
- ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, size);
- if (ret < 0)
- return ret;
-
- buf += ret;
- offset += ret;
- count -= ret;
- sum += ret;
- }
- /* here, count < sector_size because (count & ~sector_mask) == 0 */
- if (count) {
- ret = raw_pread_aligned(bs, offset, s->aligned_buf,
- bs->buffer_alignment);
- if (ret < 0)
- return ret;
- memcpy(s->aligned_buf, buf, count);
-
- ret = raw_pwrite_aligned(bs, offset, s->aligned_buf,
- bs->buffer_alignment);
- if (ret < 0)
- return ret;
- if (count < ret)
- ret = count;
-
- sum += ret;
- }
- return sum;
- }
- }
- return raw_pwrite_aligned(bs, offset, buf, count) + sum;
-}
-
-static int raw_write(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
-{
- int ret;
- ret = raw_pwrite(bs, sector_num * BDRV_SECTOR_SIZE, buf,
- nb_sectors * BDRV_SECTOR_SIZE);
- if (ret == (nb_sectors * BDRV_SECTOR_SIZE))
- ret = 0;
- return ret;
-}
-
/*
* Check if all memory in this vector is sector aligned.
*/
static int raw_truncate(BlockDriverState *bs, int64_t offset)
{
BDRVRawState *s = bs->opaque;
- if (s->type != FTYPE_FILE)
- return -ENOTSUP;
- if (ftruncate(s->fd, offset) < 0)
+ struct stat st;
+
+ if (fstat(s->fd, &st)) {
return -errno;
+ }
+
+ if (S_ISREG(st.st_mode)) {
+ if (ftruncate(s->fd, offset) < 0) {
+ return -errno;
+ }
+ } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
+ if (offset > raw_getlength(bs)) {
+ return -EINVAL;
+ }
+ } else {
+ return -ENOTSUP;
+ }
+
return 0;
}
}
if (size == 0)
#endif
-#ifdef CONFIG_COCOA
+#if defined(__APPLE__) && defined(__MACH__)
size = LONG_LONG_MAX;
#else
size = lseek(fd, 0LL, SEEK_END);
return result;
}
-static int raw_flush(BlockDriverState *bs)
+/*
+ * Returns true iff the specified sector is present in the disk image. Drivers
+ * not implementing the functionality are assumed to not support backing files,
+ * hence all their sectors are reported as allocated.
+ *
+ * If 'sector_num' is beyond the end of the disk image the return value is 0
+ * and 'pnum' is set to 0.
+ *
+ * 'pnum' is set to the number of sectors (including and immediately following
+ * the specified sector) that are known to be in the same
+ * allocated/unallocated state.
+ *
+ * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
+ * beyond the end of the disk image it will be clamped.
+ */
+static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
+ int64_t sector_num,
+ int nb_sectors, int *pnum)
{
+ off_t start, data, hole;
+ int ret;
+
+ ret = fd_open(bs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ start = sector_num * BDRV_SECTOR_SIZE;
+
+#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 1;
+ }
+
+ 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;
+ }
+
+#elif defined SEEK_HOLE && defined SEEK_DATA
+
BDRVRawState *s = bs->opaque;
- return qemu_fdatasync(s->fd);
+
+ 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 1;
+ }
+
+ 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
+ *pnum = nb_sectors;
+ return 1;
+#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);
+ return 1;
+ } else {
+ /* On a hole, compute sectors to the beginning of the next extent. */
+ *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
+ return 0;
+ }
}
#ifdef CONFIG_XFS
}
#endif
-static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
+static coroutine_fn int raw_co_discard(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors)
{
#ifdef CONFIG_XFS
BDRVRawState *s = bs->opaque;
.instance_size = sizeof(BDRVRawState),
.bdrv_probe = NULL, /* no probe for protocols */
.bdrv_file_open = raw_open,
- .bdrv_read = raw_read,
- .bdrv_write = raw_write,
.bdrv_close = raw_close,
.bdrv_create = raw_create,
- .bdrv_flush = raw_flush,
- .bdrv_discard = raw_discard,
+ .bdrv_co_discard = raw_co_discard,
+ .bdrv_co_is_allocated = raw_co_is_allocated,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
/***********************************************/
/* host device */
-#ifdef CONFIG_COCOA
+#if defined(__APPLE__) && defined(__MACH__)
static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
{
BDRVRawState *s = bs->opaque;
-#ifdef CONFIG_COCOA
+#if defined(__APPLE__) && defined(__MACH__)
if (strstart(filename, "/dev/cdrom", NULL)) {
kern_return_t kernResult;
io_iterator_t mediaIterator;
.bdrv_create = hdev_create,
.create_options = raw_create_options,
.bdrv_has_zero_init = hdev_has_zero_init,
- .bdrv_flush = raw_flush,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
- .bdrv_read = raw_read,
- .bdrv_write = raw_write,
+ .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
return ret;
}
-static void floppy_eject(BlockDriverState *bs, int eject_flag)
+static void floppy_eject(BlockDriverState *bs, bool eject_flag)
{
BDRVRawState *s = bs->opaque;
int fd;
.bdrv_create = hdev_create,
.create_options = raw_create_options,
.bdrv_has_zero_init = hdev_has_zero_init,
- .bdrv_flush = raw_flush,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
- .bdrv_read = raw_read,
- .bdrv_write = raw_write,
+ .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
return 0;
}
-static void cdrom_eject(BlockDriverState *bs, int eject_flag)
+static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
{
BDRVRawState *s = bs->opaque;
}
}
-static void cdrom_set_locked(BlockDriverState *bs, int locked)
+static void cdrom_lock_medium(BlockDriverState *bs, bool locked)
{
BDRVRawState *s = bs->opaque;
.bdrv_create = hdev_create,
.create_options = raw_create_options,
.bdrv_has_zero_init = hdev_has_zero_init,
- .bdrv_flush = raw_flush,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
- .bdrv_read = raw_read,
- .bdrv_write = raw_write,
+ .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = cdrom_is_inserted,
.bdrv_eject = cdrom_eject,
- .bdrv_set_locked = cdrom_set_locked,
+ .bdrv_lock_medium = cdrom_lock_medium,
/* generic scsi device */
.bdrv_ioctl = hdev_ioctl,
if (ret)
return ret;
- /* make sure the door isnt locked at this time */
+ /* make sure the door isn't locked at this time */
ioctl(s->fd, CDIOCALLOW);
return 0;
}
}
s->fd = fd;
- /* make sure the door isnt locked at this time */
+ /* make sure the door isn't locked at this time */
ioctl(s->fd, CDIOCALLOW);
return 0;
}
return raw_getlength(bs) > 0;
}
-static void cdrom_eject(BlockDriverState *bs, int eject_flag)
+static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
{
BDRVRawState *s = bs->opaque;
cdrom_reopen(bs);
}
-static void cdrom_set_locked(BlockDriverState *bs, int locked)
+static void cdrom_lock_medium(BlockDriverState *bs, bool locked)
{
BDRVRawState *s = bs->opaque;
.bdrv_create = hdev_create,
.create_options = raw_create_options,
.bdrv_has_zero_init = hdev_has_zero_init,
- .bdrv_flush = raw_flush,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
- .bdrv_read = raw_read,
- .bdrv_write = raw_write,
+ .bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = cdrom_is_inserted,
.bdrv_eject = cdrom_eject,
- .bdrv_set_locked = cdrom_set_locked,
+ .bdrv_lock_medium = cdrom_lock_medium,
};
#endif /* __FreeBSD__ */