kvm tools: Fix large disk images on 32-bit

Use read()/write() instead of mmap() for virtio block device emulation to
support large disk images on 32-bit.

Reported-and-tested-by: Asias He <asias.hejun@gmail.com>
Tested-by: Cyrill Gorcunov <gorcunov@gmail.com>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
This commit is contained in:
Pekka Enberg
2015-06-01 16:39:41 +01:00
committed by Will Deacon
parent db34045c54
commit c4d7847b22
5 changed files with 108 additions and 27 deletions
+4
View File
@@ -15,6 +15,7 @@ TAGS = ctags
OBJS += 8250-serial.o
OBJS += blk-virtio.o
OBJS += cpuid.o
OBJS += read-write.o
OBJS += disk-image.o
OBJS += interrupt.o
OBJS += ioport.o
@@ -40,6 +41,9 @@ ifeq ($(uname_M),x86_64)
endif
endif
DEFINES += -D_FILE_OFFSET_BITS=64
DEFINES += -D_GNU_SOURCE
CFLAGS += $(CPPFLAGS) $(DEFINES) -Iinclude -I../../include -Os -g
WARNINGS += -Werror
+12 -26
View File
@@ -1,5 +1,6 @@
#include "kvm/disk-image.h"
#include "kvm/read-write.h"
#include "kvm/util.h"
#include <sys/types.h>
@@ -12,19 +13,6 @@
#include <unistd.h>
#include <fcntl.h>
static const char QCOW_MAGIC[] = { 'Q', 'F', 'I', 0xfb };
struct qcow_header {
uint8_t magic[5];
};
static bool disk_image__is_qcow(struct disk_image *self)
{
struct qcow_header *header = self->mmap;
return !memcmp(header->magic, QCOW_MAGIC, ARRAY_SIZE(QCOW_MAGIC));
}
struct disk_image *disk_image__open(const char *filename)
{
struct disk_image *self;
@@ -34,7 +22,7 @@ struct disk_image *disk_image__open(const char *filename)
if (!self)
return NULL;
self->fd = open(filename, O_RDONLY);
self->fd = open(filename, O_RDWR);
if (self->fd < 0)
goto failed_free;
@@ -43,13 +31,6 @@ struct disk_image *disk_image__open(const char *filename)
self->size = st.st_size;
self->mmap = mmap(NULL, self->size, PROT_READ|PROT_WRITE, MAP_PRIVATE, self->fd, 0);
if (self->mmap == MAP_FAILED)
goto failed_close_fd;
if (disk_image__is_qcow(self))
die("QCOW disk image format is not supported.");
return self;
failed_close_fd:
@@ -62,9 +43,6 @@ failed_free:
void disk_image__close(struct disk_image *self)
{
if (munmap(self->mmap, self->size) < 0)
warning("munmap() failed");
if (close(self->fd) < 0)
warning("close() failed");
@@ -78,7 +56,11 @@ int disk_image__read_sector(struct disk_image *self, uint64_t sector, void *dst,
if (offset + dst_len > self->size)
return -1;
memcpy(dst, self->mmap + offset, dst_len);
if (lseek(self->fd, offset, SEEK_SET) < 0)
return -1;
if (read_in_full(self->fd, dst, dst_len) < 0)
return -1;
return 0;
}
@@ -90,7 +72,11 @@ int disk_image__write_sector(struct disk_image *self, uint64_t sector, void *src
if (offset + src_len > self->size)
return -1;
memcpy(self->mmap + offset, src, src_len);
if (lseek(self->fd, offset, SEEK_SET) < 0)
return -1;
if (write_in_full(self->fd, src, src_len) < 0)
return -1;
return 0;
}
-1
View File
@@ -7,7 +7,6 @@
#define SECTOR_SIZE (1UL << SECTOR_SHIFT)
struct disk_image {
void *mmap;
int fd;
uint64_t size;
};
+13
View File
@@ -0,0 +1,13 @@
#ifndef KVM_READ_WRITE_H
#define KVM_READ_WRITE_H
#include <sys/types.h>
#include <unistd.h>
ssize_t xread(int fd, void *buf, size_t count);
ssize_t xwrite(int fd, const void *buf, size_t count);
ssize_t read_in_full(int fd, void *buf, size_t count);
ssize_t write_in_full(int fd, const void *buf, size_t count);
#endif /* KVM_READ_WRITE_H */
+79
View File
@@ -0,0 +1,79 @@
#include "kvm/read-write.h"
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
/* Same as read(2) except that this function never returns EAGAIN or EINTR. */
ssize_t xread(int fd, void *buf, size_t count)
{
ssize_t nr;
restart:
nr = read(fd, buf, count);
if ((nr < 0) && ((errno == EAGAIN) || (errno == EINTR)))
goto restart;
return nr;
}
/* Same as write(2) except that this function never returns EAGAIN or EINTR. */
ssize_t xwrite(int fd, const void *buf, size_t count)
{
ssize_t nr;
restart:
nr = write(fd, buf, count);
if ((nr < 0) && ((errno == EAGAIN) || (errno == EINTR)))
goto restart;
return nr;
}
ssize_t read_in_full(int fd, void *buf, size_t count)
{
ssize_t total = 0;
char *p = buf;
while (count > 0) {
ssize_t nr;
nr = xread(fd, p, count);
if (nr <= 0) {
if (total > 0)
return total;
return -1;
}
count -= nr;
total += nr;
p += nr;
}
return total;
}
ssize_t write_in_full(int fd, const void *buf, size_t count)
{
const char *p = buf;
ssize_t total = 0;
while (count > 0) {
ssize_t nr;
nr = xwrite(fd, p, count);
if (nr < 0)
return -1;
if (nr == 0) {
errno = ENOSPC;
return -1;
}
count -= nr;
total += nr;
p += nr;
}
return total;
}