xref: /kvmtool/disk/core.c (revision 43835ac90fb7c14c3946772d778da4b62a0bfd7c)
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 
76ec60cfdSPekka Enberg #include <linux/fs.h>	/* for BLKGETSIZE64 */
86ec60cfdSPekka Enberg 
96ec60cfdSPekka Enberg #include <sys/ioctl.h>
109f532d00SPekka Enberg #include <sys/types.h>
113fdf659dSSasha Levin #include <linux/types.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 
203fdf659dSSasha Levin struct disk_image *disk_image__new(int fd, u64 size, struct disk_image_operations *ops)
219f532d00SPekka Enberg {
22*43835ac9SSasha Levin 	struct disk_image *disk;
239f532d00SPekka Enberg 
24*43835ac9SSasha Levin 	disk		= malloc(sizeof *disk);
25*43835ac9SSasha Levin 	if (!disk)
26fffb37a9SPekka Enberg 		return NULL;
279f532d00SPekka Enberg 
28*43835ac9SSasha Levin 	disk->fd	= fd;
29*43835ac9SSasha Levin 	disk->size	= size;
30*43835ac9SSasha Levin 	disk->ops	= ops;
31*43835ac9SSasha Levin 	return disk;
32f4ff38dfSPrasad Joshi }
33f4ff38dfSPrasad Joshi 
343fdf659dSSasha Levin struct disk_image *disk_image__new_readonly(int fd, u64 size, struct disk_image_operations *ops)
35f4ff38dfSPrasad Joshi {
36*43835ac9SSasha Levin 	struct disk_image *disk;
37f4ff38dfSPrasad Joshi 
38*43835ac9SSasha Levin 	disk = disk_image__new(fd, size, ops);
39*43835ac9SSasha Levin 	if (!disk)
40f4ff38dfSPrasad Joshi 		return NULL;
41f4ff38dfSPrasad Joshi 
42*43835ac9SSasha Levin 	disk->priv = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, fd, 0);
43*43835ac9SSasha Levin 	if (disk->priv == MAP_FAILED)
449ac38fe1SSasha Levin 		die("mmap() failed");
45*43835ac9SSasha Levin 	return disk;
469f532d00SPekka Enberg }
479f532d00SPekka Enberg 
48*43835ac9SSasha Levin static ssize_t raw_image__read_sector_iov(struct disk_image *disk, u64 sector, const struct iovec *iov, int iovcount)
4970b53f25SSasha Levin {
503fdf659dSSasha Levin 	u64 offset = sector << SECTOR_SHIFT;
5170b53f25SSasha Levin 
52*43835ac9SSasha Levin 	return preadv_in_full(disk->fd, iov, iovcount, offset);
5370b53f25SSasha Levin }
5470b53f25SSasha Levin 
55*43835ac9SSasha Levin static ssize_t raw_image__write_sector_iov(struct disk_image *disk, u64 sector, const struct iovec *iov, int iovcount)
5670b53f25SSasha Levin {
573fdf659dSSasha Levin 	u64 offset = sector << SECTOR_SHIFT;
5870b53f25SSasha Levin 
59*43835ac9SSasha Levin 	return pwritev_in_full(disk->fd, iov, iovcount, offset);
6070b53f25SSasha Levin }
6170b53f25SSasha Levin 
62*43835ac9SSasha Levin static int raw_image__read_sector_ro_mmap(struct disk_image *disk, u64 sector, void *dst, u32 dst_len)
639ac38fe1SSasha Levin {
643fdf659dSSasha Levin 	u64 offset = sector << SECTOR_SHIFT;
659ac38fe1SSasha Levin 
66*43835ac9SSasha Levin 	if (offset + dst_len > disk->size)
679ac38fe1SSasha Levin 		return -1;
689ac38fe1SSasha Levin 
69*43835ac9SSasha Levin 	memcpy(dst, disk->priv + offset, dst_len);
709ac38fe1SSasha Levin 
719ac38fe1SSasha Levin 	return 0;
729ac38fe1SSasha Levin }
739ac38fe1SSasha Levin 
74*43835ac9SSasha Levin static int raw_image__write_sector_ro_mmap(struct disk_image *disk, u64 sector, void *src, u32 src_len)
759ac38fe1SSasha Levin {
763fdf659dSSasha Levin 	u64 offset = sector << SECTOR_SHIFT;
779ac38fe1SSasha Levin 
78*43835ac9SSasha Levin 	if (offset + src_len > disk->size)
799ac38fe1SSasha Levin 		return -1;
809ac38fe1SSasha Levin 
81*43835ac9SSasha Levin 	memcpy(disk->priv + offset, src, src_len);
829ac38fe1SSasha Levin 
839ac38fe1SSasha Levin 	return 0;
849ac38fe1SSasha Levin }
859ac38fe1SSasha Levin 
86*43835ac9SSasha Levin static void raw_image__close_ro_mmap(struct disk_image *disk)
87177a5815SPrasad Joshi {
88*43835ac9SSasha Levin 	if (disk->priv != MAP_FAILED)
89*43835ac9SSasha Levin 		munmap(disk->priv, disk->size);
90177a5815SPrasad Joshi }
91177a5815SPrasad Joshi 
92499f3bedSPekka Enberg static struct disk_image_operations raw_image_ops = {
932d103098SSasha Levin 	.read_sector_iov	= raw_image__read_sector_iov,
942d103098SSasha Levin 	.write_sector_iov	= raw_image__write_sector_iov
95499f3bedSPekka Enberg };
96499f3bedSPekka Enberg 
979ac38fe1SSasha Levin static struct disk_image_operations raw_image_ro_mmap_ops = {
989ac38fe1SSasha Levin 	.read_sector		= raw_image__read_sector_ro_mmap,
999ac38fe1SSasha Levin 	.write_sector		= raw_image__write_sector_ro_mmap,
1000ff5e559SSasha Levin 	.close			= raw_image__close_ro_mmap,
1019ac38fe1SSasha Levin };
1029ac38fe1SSasha Levin 
1036ec60cfdSPekka Enberg static struct disk_image *raw_image__probe(int fd, struct stat *st, bool readonly)
104499f3bedSPekka Enberg {
1056ec60cfdSPekka Enberg 	if (readonly)
1066ec60cfdSPekka Enberg 		return disk_image__new_readonly(fd, st->st_size, &raw_image_ro_mmap_ops);
1076ec60cfdSPekka Enberg 	else
1086ec60cfdSPekka Enberg 		return disk_image__new(fd, st->st_size, &raw_image_ops);
1096ec60cfdSPekka Enberg }
110499f3bedSPekka Enberg 
1116ec60cfdSPekka Enberg static struct disk_image *blkdev__probe(const char *filename, struct stat *st)
1126ec60cfdSPekka Enberg {
1133fdf659dSSasha Levin 	u64 size;
1146ec60cfdSPekka Enberg 	int fd;
1156ec60cfdSPekka Enberg 
1166ec60cfdSPekka Enberg 	if (!S_ISBLK(st->st_mode))
117499f3bedSPekka Enberg 		return NULL;
118499f3bedSPekka Enberg 
1196ec60cfdSPekka Enberg 	fd		= open(filename, O_RDONLY);
1206ec60cfdSPekka Enberg 	if (fd < 0)
1216ec60cfdSPekka Enberg 		return NULL;
1226ec60cfdSPekka Enberg 
12372756070SSasha Levin 	if (ioctl(fd, BLKGETSIZE64, &size) < 0) {
12472756070SSasha Levin 		close(fd);
1256ec60cfdSPekka Enberg 		return NULL;
12672756070SSasha Levin 	}
1276ec60cfdSPekka Enberg 
1286ec60cfdSPekka Enberg 	return disk_image__new_readonly(fd, size, &raw_image_ro_mmap_ops);
129499f3bedSPekka Enberg }
130499f3bedSPekka Enberg 
1319ac38fe1SSasha Levin struct disk_image *disk_image__open(const char *filename, bool readonly)
132499f3bedSPekka Enberg {
133*43835ac9SSasha Levin 	struct disk_image *disk;
1346ec60cfdSPekka Enberg 	struct stat st;
135499f3bedSPekka Enberg 	int fd;
136499f3bedSPekka Enberg 
1376ec60cfdSPekka Enberg 	if (stat(filename, &st) < 0)
1386ec60cfdSPekka Enberg 		return NULL;
1396ec60cfdSPekka Enberg 
1406ec60cfdSPekka Enberg 	if (S_ISBLK(st.st_mode))
1416ec60cfdSPekka Enberg 		return blkdev__probe(filename, &st);
1426ec60cfdSPekka Enberg 
1439ac38fe1SSasha Levin 	fd		= open(filename, readonly ? O_RDONLY : O_RDWR);
144499f3bedSPekka Enberg 	if (fd < 0)
145499f3bedSPekka Enberg 		return NULL;
146499f3bedSPekka Enberg 
147*43835ac9SSasha Levin 	disk = qcow_probe(fd);
148*43835ac9SSasha Levin 	if (disk)
149*43835ac9SSasha Levin 		return disk;
15086835cedSPrasad Joshi 
151*43835ac9SSasha Levin 	disk = raw_image__probe(fd, &st, readonly);
152*43835ac9SSasha Levin 	if (disk)
153*43835ac9SSasha Levin 		return disk;
154499f3bedSPekka Enberg 
155d6c58e5bSPrasad Joshi 	if (close(fd) < 0)
156499f3bedSPekka Enberg 		warning("close() failed");
157499f3bedSPekka Enberg 
158499f3bedSPekka Enberg 	return NULL;
159499f3bedSPekka Enberg }
160499f3bedSPekka Enberg 
161*43835ac9SSasha Levin void disk_image__close(struct disk_image *disk)
162499f3bedSPekka Enberg {
16382d94c50SIngo Molnar 	/* If there was no disk image then there's nothing to do: */
164*43835ac9SSasha Levin 	if (!disk)
16582d94c50SIngo Molnar 		return;
16682d94c50SIngo Molnar 
167*43835ac9SSasha Levin 	if (disk->ops->close)
168*43835ac9SSasha Levin 		disk->ops->close(disk);
169499f3bedSPekka Enberg 
170*43835ac9SSasha Levin 	if (close(disk->fd) < 0)
171499f3bedSPekka Enberg 		warning("close() failed");
172499f3bedSPekka Enberg 
173*43835ac9SSasha Levin 	free(disk);
174499f3bedSPekka Enberg }
175