xref: /kvmtool/disk/core.c (revision 6ec60cfdc448373f877eccca1b1c9e2f4deabfe8)
19f532d00SPekka Enberg #include "kvm/disk-image.h"
29f532d00SPekka Enberg 
3c4d7847bSPekka Enberg #include "kvm/read-write.h"
486835cedSPrasad Joshi #include "kvm/qcow.h"
59f532d00SPekka Enberg #include "kvm/util.h"
69f532d00SPekka Enberg 
7*6ec60cfdSPekka Enberg #include <linux/fs.h>	/* for BLKGETSIZE64 */
8*6ec60cfdSPekka Enberg 
9*6ec60cfdSPekka Enberg #include <sys/ioctl.h>
109f532d00SPekka Enberg #include <sys/types.h>
1133d33901SPekka Enberg #include <inttypes.h>
129f532d00SPekka Enberg #include <sys/mman.h>
139f532d00SPekka Enberg #include <sys/stat.h>
145646450dSPekka Enberg #include <stdbool.h>
159f532d00SPekka Enberg #include <stddef.h>
169f532d00SPekka Enberg #include <stdlib.h>
179f532d00SPekka Enberg #include <unistd.h>
189f532d00SPekka Enberg #include <fcntl.h>
199f532d00SPekka Enberg 
20f4ff38dfSPrasad Joshi struct disk_image *disk_image__new(int fd, uint64_t size, struct disk_image_operations *ops)
219f532d00SPekka Enberg {
22fffb37a9SPekka Enberg 	struct disk_image *self;
239f532d00SPekka Enberg 
24fffb37a9SPekka Enberg 	self		= malloc(sizeof *self);
25fffb37a9SPekka Enberg 	if (!self)
26fffb37a9SPekka Enberg 		return NULL;
279f532d00SPekka Enberg 
28fffb37a9SPekka Enberg 	self->fd	= fd;
29fffb37a9SPekka Enberg 	self->size	= size;
30fffb37a9SPekka Enberg 	self->ops	= ops;
31f4ff38dfSPrasad Joshi 	return self;
32f4ff38dfSPrasad Joshi }
33f4ff38dfSPrasad Joshi 
34f4ff38dfSPrasad Joshi struct disk_image *disk_image__new_readonly(int fd, uint64_t size, struct disk_image_operations *ops)
35f4ff38dfSPrasad Joshi {
36f4ff38dfSPrasad Joshi 	struct disk_image *self;
37f4ff38dfSPrasad Joshi 
38f4ff38dfSPrasad Joshi 	self = disk_image__new(fd, size, ops);
39f4ff38dfSPrasad Joshi 	if (!self)
40f4ff38dfSPrasad Joshi 		return NULL;
41f4ff38dfSPrasad Joshi 
429ac38fe1SSasha Levin 	self->priv = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, fd, 0);
439ac38fe1SSasha Levin 	if (self->priv == MAP_FAILED)
449ac38fe1SSasha Levin 		die("mmap() failed");
45fffb37a9SPekka Enberg 	return self;
469f532d00SPekka Enberg }
479f532d00SPekka Enberg 
48f4ff38dfSPrasad Joshi 
49499f3bedSPekka Enberg static int raw_image__read_sector(struct disk_image *self, uint64_t sector, void *dst, uint32_t dst_len)
509f532d00SPekka Enberg {
519f532d00SPekka Enberg 	uint64_t offset = sector << SECTOR_SHIFT;
529f532d00SPekka Enberg 
535a24a9f2SPekka Enberg 	if (offset + dst_len > self->size)
549f532d00SPekka Enberg 		return -1;
559f532d00SPekka Enberg 
566b7deb02SPekka Enberg 	if (pread_in_full(self->fd, dst, dst_len, offset) < 0)
57c4d7847bSPekka Enberg 		return -1;
589f532d00SPekka Enberg 
599f532d00SPekka Enberg 	return 0;
609f532d00SPekka Enberg }
61258dd093SPekka Enberg 
62499f3bedSPekka Enberg static int raw_image__write_sector(struct disk_image *self, uint64_t sector, void *src, uint32_t src_len)
63258dd093SPekka Enberg {
64258dd093SPekka Enberg 	uint64_t offset = sector << SECTOR_SHIFT;
65258dd093SPekka Enberg 
66258dd093SPekka Enberg 	if (offset + src_len > self->size)
67258dd093SPekka Enberg 		return -1;
68258dd093SPekka Enberg 
696b7deb02SPekka Enberg 	if (pwrite_in_full(self->fd, src, src_len, offset) < 0)
70c4d7847bSPekka Enberg 		return -1;
71258dd093SPekka Enberg 
72258dd093SPekka Enberg 	return 0;
73258dd093SPekka Enberg }
74499f3bedSPekka Enberg 
759ac38fe1SSasha Levin static int raw_image__read_sector_ro_mmap(struct disk_image *self, uint64_t sector, void *dst, uint32_t dst_len)
769ac38fe1SSasha Levin {
779ac38fe1SSasha Levin 	uint64_t offset = sector << SECTOR_SHIFT;
789ac38fe1SSasha Levin 
799ac38fe1SSasha Levin 	if (offset + dst_len > self->size)
809ac38fe1SSasha Levin 		return -1;
819ac38fe1SSasha Levin 
829ac38fe1SSasha Levin 	memcpy(dst, self->priv + offset, dst_len);
839ac38fe1SSasha Levin 
849ac38fe1SSasha Levin 	return 0;
859ac38fe1SSasha Levin }
869ac38fe1SSasha Levin 
879ac38fe1SSasha Levin static int raw_image__write_sector_ro_mmap(struct disk_image *self, uint64_t sector, void *src, uint32_t src_len)
889ac38fe1SSasha Levin {
899ac38fe1SSasha Levin 	uint64_t offset = sector << SECTOR_SHIFT;
909ac38fe1SSasha Levin 
919ac38fe1SSasha Levin 	if (offset + src_len > self->size)
929ac38fe1SSasha Levin 		return -1;
939ac38fe1SSasha Levin 
949ac38fe1SSasha Levin 	memcpy(self->priv + offset, src, src_len);
959ac38fe1SSasha Levin 
969ac38fe1SSasha Levin 	return 0;
979ac38fe1SSasha Levin }
989ac38fe1SSasha Levin 
99177a5815SPrasad Joshi static void raw_image__close_sector_ro_mmap(struct disk_image *self)
100177a5815SPrasad Joshi {
101177a5815SPrasad Joshi 	if (self->priv != MAP_FAILED)
102177a5815SPrasad Joshi 		munmap(self->priv, self->size);
103177a5815SPrasad Joshi }
104177a5815SPrasad Joshi 
105499f3bedSPekka Enberg static struct disk_image_operations raw_image_ops = {
106499f3bedSPekka Enberg 	.read_sector		= raw_image__read_sector,
107499f3bedSPekka Enberg 	.write_sector		= raw_image__write_sector,
108499f3bedSPekka Enberg };
109499f3bedSPekka Enberg 
1109ac38fe1SSasha Levin static struct disk_image_operations raw_image_ro_mmap_ops = {
1119ac38fe1SSasha Levin 	.read_sector		= raw_image__read_sector_ro_mmap,
1129ac38fe1SSasha Levin 	.write_sector		= raw_image__write_sector_ro_mmap,
113177a5815SPrasad Joshi 	.close			= raw_image__close_sector_ro_mmap,
1149ac38fe1SSasha Levin };
1159ac38fe1SSasha Levin 
116*6ec60cfdSPekka Enberg static struct disk_image *raw_image__probe(int fd, struct stat *st, bool readonly)
117499f3bedSPekka Enberg {
118*6ec60cfdSPekka Enberg 	if (readonly)
119*6ec60cfdSPekka Enberg 		return disk_image__new_readonly(fd, st->st_size, &raw_image_ro_mmap_ops);
120*6ec60cfdSPekka Enberg 	else
121*6ec60cfdSPekka Enberg 		return disk_image__new(fd, st->st_size, &raw_image_ops);
122*6ec60cfdSPekka Enberg }
123499f3bedSPekka Enberg 
124*6ec60cfdSPekka Enberg static struct disk_image *blkdev__probe(const char *filename, struct stat *st)
125*6ec60cfdSPekka Enberg {
126*6ec60cfdSPekka Enberg 	uint64_t size;
127*6ec60cfdSPekka Enberg 	int fd;
128*6ec60cfdSPekka Enberg 
129*6ec60cfdSPekka Enberg 	if (!S_ISBLK(st->st_mode))
130499f3bedSPekka Enberg 		return NULL;
131499f3bedSPekka Enberg 
132*6ec60cfdSPekka Enberg 	fd		= open(filename, O_RDONLY);
133*6ec60cfdSPekka Enberg 	if (fd < 0)
134*6ec60cfdSPekka Enberg 		return NULL;
135*6ec60cfdSPekka Enberg 
136*6ec60cfdSPekka Enberg 	if (ioctl(fd, BLKGETSIZE64, &size) < 0)
137*6ec60cfdSPekka Enberg 		return NULL;
138*6ec60cfdSPekka Enberg 
139*6ec60cfdSPekka Enberg 	return disk_image__new_readonly(fd, size, &raw_image_ro_mmap_ops);
140499f3bedSPekka Enberg }
141499f3bedSPekka Enberg 
1429ac38fe1SSasha Levin struct disk_image *disk_image__open(const char *filename, bool readonly)
143499f3bedSPekka Enberg {
144499f3bedSPekka Enberg 	struct disk_image *self;
145*6ec60cfdSPekka Enberg 	struct stat st;
146499f3bedSPekka Enberg 	int fd;
147499f3bedSPekka Enberg 
148*6ec60cfdSPekka Enberg 	if (stat(filename, &st) < 0)
149*6ec60cfdSPekka Enberg 		return NULL;
150*6ec60cfdSPekka Enberg 
151*6ec60cfdSPekka Enberg 	if (S_ISBLK(st.st_mode))
152*6ec60cfdSPekka Enberg 		return blkdev__probe(filename, &st);
153*6ec60cfdSPekka Enberg 
1549ac38fe1SSasha Levin 	fd		= open(filename, readonly ? O_RDONLY : O_RDWR);
155499f3bedSPekka Enberg 	if (fd < 0)
156499f3bedSPekka Enberg 		return NULL;
157499f3bedSPekka Enberg 
15886835cedSPrasad Joshi 	self = qcow_probe(fd);
15986835cedSPrasad Joshi 	if (self)
16086835cedSPrasad Joshi 		return self;
16186835cedSPrasad Joshi 
162*6ec60cfdSPekka Enberg 	self = raw_image__probe(fd, &st, readonly);
163499f3bedSPekka Enberg 	if (self)
164499f3bedSPekka Enberg 		return self;
165499f3bedSPekka Enberg 
166d6c58e5bSPrasad Joshi 	if (close(fd) < 0)
167499f3bedSPekka Enberg 		warning("close() failed");
168499f3bedSPekka Enberg 
169499f3bedSPekka Enberg 	return NULL;
170499f3bedSPekka Enberg }
171499f3bedSPekka Enberg 
172499f3bedSPekka Enberg void disk_image__close(struct disk_image *self)
173499f3bedSPekka Enberg {
17482d94c50SIngo Molnar 	/* If there was no disk image then there's nothing to do: */
17582d94c50SIngo Molnar 	if (!self)
17682d94c50SIngo Molnar 		return;
17782d94c50SIngo Molnar 
178499f3bedSPekka Enberg 	if (self->ops->close)
179499f3bedSPekka Enberg 		self->ops->close(self);
180499f3bedSPekka Enberg 
181499f3bedSPekka Enberg 	if (close(self->fd) < 0)
182499f3bedSPekka Enberg 		warning("close() failed");
183499f3bedSPekka Enberg 
184499f3bedSPekka Enberg 	free(self);
185499f3bedSPekka Enberg }
186