xref: /kvmtool/disk/core.c (revision 9ac38fe149040a68a4255b3d7a8d3092ea77c099)
19f532d00SPekka Enberg #include "kvm/disk-image.h"
29f532d00SPekka Enberg 
3c4d7847bSPekka Enberg #include "kvm/read-write.h"
49f532d00SPekka Enberg #include "kvm/util.h"
59f532d00SPekka Enberg 
69f532d00SPekka Enberg #include <sys/types.h>
733d33901SPekka Enberg #include <inttypes.h>
89f532d00SPekka Enberg #include <sys/mman.h>
99f532d00SPekka Enberg #include <sys/stat.h>
105646450dSPekka Enberg #include <stdbool.h>
119f532d00SPekka Enberg #include <stddef.h>
129f532d00SPekka Enberg #include <stdlib.h>
139f532d00SPekka Enberg #include <unistd.h>
149f532d00SPekka Enberg #include <fcntl.h>
159f532d00SPekka Enberg 
16*9ac38fe1SSasha Levin struct disk_image *disk_image__new(int fd, uint64_t size, struct disk_image_operations *ops, bool readonly)
179f532d00SPekka Enberg {
18fffb37a9SPekka Enberg 	struct disk_image *self;
199f532d00SPekka Enberg 
20fffb37a9SPekka Enberg 	self		= malloc(sizeof *self);
21fffb37a9SPekka Enberg 	if (!self)
22fffb37a9SPekka Enberg 		return NULL;
239f532d00SPekka Enberg 
24fffb37a9SPekka Enberg 	self->fd	= fd;
25fffb37a9SPekka Enberg 	self->size	= size;
26fffb37a9SPekka Enberg 	self->ops	= ops;
27*9ac38fe1SSasha Levin 	if (readonly) {
28*9ac38fe1SSasha Levin 		self->priv = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, fd, 0);
29*9ac38fe1SSasha Levin 		if (self->priv == MAP_FAILED)
30*9ac38fe1SSasha Levin 			die("mmap() failed");
31*9ac38fe1SSasha Levin 	} else
32*9ac38fe1SSasha Levin 		self->priv = MAP_FAILED;
339f532d00SPekka Enberg 
34fffb37a9SPekka Enberg 	return self;
359f532d00SPekka Enberg }
369f532d00SPekka Enberg 
37499f3bedSPekka Enberg static int raw_image__read_sector(struct disk_image *self, uint64_t sector, void *dst, uint32_t dst_len)
389f532d00SPekka Enberg {
399f532d00SPekka Enberg 	uint64_t offset = sector << SECTOR_SHIFT;
409f532d00SPekka Enberg 
415a24a9f2SPekka Enberg 	if (offset + dst_len > self->size)
429f532d00SPekka Enberg 		return -1;
439f532d00SPekka Enberg 
446b7deb02SPekka Enberg 	if (pread_in_full(self->fd, dst, dst_len, offset) < 0)
45c4d7847bSPekka Enberg 		return -1;
469f532d00SPekka Enberg 
479f532d00SPekka Enberg 	return 0;
489f532d00SPekka Enberg }
49258dd093SPekka Enberg 
50499f3bedSPekka Enberg static int raw_image__write_sector(struct disk_image *self, uint64_t sector, void *src, uint32_t src_len)
51258dd093SPekka Enberg {
52258dd093SPekka Enberg 	uint64_t offset = sector << SECTOR_SHIFT;
53258dd093SPekka Enberg 
54258dd093SPekka Enberg 	if (offset + src_len > self->size)
55258dd093SPekka Enberg 		return -1;
56258dd093SPekka Enberg 
576b7deb02SPekka Enberg 	if (pwrite_in_full(self->fd, src, src_len, offset) < 0)
58c4d7847bSPekka Enberg 		return -1;
59258dd093SPekka Enberg 
60258dd093SPekka Enberg 	return 0;
61258dd093SPekka Enberg }
62499f3bedSPekka Enberg 
63*9ac38fe1SSasha Levin static int raw_image__read_sector_ro_mmap(struct disk_image *self, uint64_t sector, void *dst, uint32_t dst_len)
64*9ac38fe1SSasha Levin {
65*9ac38fe1SSasha Levin 	uint64_t offset = sector << SECTOR_SHIFT;
66*9ac38fe1SSasha Levin 
67*9ac38fe1SSasha Levin 	if (offset + dst_len > self->size)
68*9ac38fe1SSasha Levin 		return -1;
69*9ac38fe1SSasha Levin 
70*9ac38fe1SSasha Levin 	memcpy(dst, self->priv + offset, dst_len);
71*9ac38fe1SSasha Levin 
72*9ac38fe1SSasha Levin 	return 0;
73*9ac38fe1SSasha Levin }
74*9ac38fe1SSasha Levin 
75*9ac38fe1SSasha Levin static int raw_image__write_sector_ro_mmap(struct disk_image *self, uint64_t sector, void *src, uint32_t src_len)
76*9ac38fe1SSasha Levin {
77*9ac38fe1SSasha Levin 	uint64_t offset = sector << SECTOR_SHIFT;
78*9ac38fe1SSasha Levin 
79*9ac38fe1SSasha Levin 	if (offset + src_len > self->size)
80*9ac38fe1SSasha Levin 		return -1;
81*9ac38fe1SSasha Levin 
82*9ac38fe1SSasha Levin 	memcpy(self->priv + offset, src, src_len);
83*9ac38fe1SSasha Levin 
84*9ac38fe1SSasha Levin 	return 0;
85*9ac38fe1SSasha Levin }
86*9ac38fe1SSasha Levin 
87499f3bedSPekka Enberg static struct disk_image_operations raw_image_ops = {
88499f3bedSPekka Enberg 	.read_sector		= raw_image__read_sector,
89499f3bedSPekka Enberg 	.write_sector		= raw_image__write_sector,
90499f3bedSPekka Enberg };
91499f3bedSPekka Enberg 
92*9ac38fe1SSasha Levin static struct disk_image_operations raw_image_ro_mmap_ops = {
93*9ac38fe1SSasha Levin 	.read_sector		= raw_image__read_sector_ro_mmap,
94*9ac38fe1SSasha Levin 	.write_sector		= raw_image__write_sector_ro_mmap,
95*9ac38fe1SSasha Levin };
96*9ac38fe1SSasha Levin 
97*9ac38fe1SSasha Levin static struct disk_image *raw_image__probe(int fd, bool readonly)
98499f3bedSPekka Enberg {
99499f3bedSPekka Enberg 	struct stat st;
100499f3bedSPekka Enberg 
101499f3bedSPekka Enberg 	if (fstat(fd, &st) < 0)
102499f3bedSPekka Enberg 		return NULL;
103499f3bedSPekka Enberg 
104*9ac38fe1SSasha Levin 	return disk_image__new(fd, st.st_size, readonly ? &raw_image_ro_mmap_ops : &raw_image_ops, readonly);
105499f3bedSPekka Enberg }
106499f3bedSPekka Enberg 
107*9ac38fe1SSasha Levin struct disk_image *disk_image__open(const char *filename, bool readonly)
108499f3bedSPekka Enberg {
109499f3bedSPekka Enberg 	struct disk_image *self;
110499f3bedSPekka Enberg 	int fd;
111499f3bedSPekka Enberg 
112*9ac38fe1SSasha Levin 	fd		= open(filename, readonly ? O_RDONLY : O_RDWR);
113499f3bedSPekka Enberg 	if (fd < 0)
114499f3bedSPekka Enberg 		return NULL;
115499f3bedSPekka Enberg 
116*9ac38fe1SSasha Levin 	self = raw_image__probe(fd, readonly);
117499f3bedSPekka Enberg 	if (self)
118499f3bedSPekka Enberg 		return self;
119499f3bedSPekka Enberg 
120d6c58e5bSPrasad Joshi 	if (close(fd) < 0)
121499f3bedSPekka Enberg 		warning("close() failed");
122499f3bedSPekka Enberg 
123499f3bedSPekka Enberg 	return NULL;
124499f3bedSPekka Enberg }
125499f3bedSPekka Enberg 
126499f3bedSPekka Enberg void disk_image__close(struct disk_image *self)
127499f3bedSPekka Enberg {
12882d94c50SIngo Molnar 	/* If there was no disk image then there's nothing to do: */
12982d94c50SIngo Molnar 	if (!self)
13082d94c50SIngo Molnar 		return;
13182d94c50SIngo Molnar 
132*9ac38fe1SSasha Levin 	if (self->priv != MAP_FAILED)
133*9ac38fe1SSasha Levin 		munmap(self->priv, self->size);
134*9ac38fe1SSasha Levin 
135499f3bedSPekka Enberg 	if (self->ops->close)
136499f3bedSPekka Enberg 		self->ops->close(self);
137499f3bedSPekka Enberg 
138499f3bedSPekka Enberg 	if (close(self->fd) < 0)
139499f3bedSPekka Enberg 		warning("close() failed");
140499f3bedSPekka Enberg 
141499f3bedSPekka Enberg 	free(self);
142499f3bedSPekka Enberg }
143