xref: /kvmtool/disk/core.c (revision 3fdf659d959233a543d1f29a358f8da994cec0fb)
1 #include "kvm/disk-image.h"
2 
3 #include "kvm/read-write.h"
4 #include "kvm/qcow.h"
5 #include "kvm/util.h"
6 
7 #include <linux/fs.h>	/* for BLKGETSIZE64 */
8 
9 #include <sys/ioctl.h>
10 #include <sys/types.h>
11 #include <linux/types.h>
12 #include <sys/mman.h>
13 #include <sys/stat.h>
14 #include <stdbool.h>
15 #include <stddef.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 
20 struct disk_image *disk_image__new(int fd, u64 size, struct disk_image_operations *ops)
21 {
22 	struct disk_image *self;
23 
24 	self		= malloc(sizeof *self);
25 	if (!self)
26 		return NULL;
27 
28 	self->fd	= fd;
29 	self->size	= size;
30 	self->ops	= ops;
31 	return self;
32 }
33 
34 struct disk_image *disk_image__new_readonly(int fd, u64 size, struct disk_image_operations *ops)
35 {
36 	struct disk_image *self;
37 
38 	self = disk_image__new(fd, size, ops);
39 	if (!self)
40 		return NULL;
41 
42 	self->priv = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, fd, 0);
43 	if (self->priv == MAP_FAILED)
44 		die("mmap() failed");
45 	return self;
46 }
47 
48 static ssize_t raw_image__read_sector_iov(struct disk_image *self, u64 sector, const struct iovec *iov, int iovcount)
49 {
50 	u64 offset = sector << SECTOR_SHIFT;
51 
52 	return preadv_in_full(self->fd, iov, iovcount, offset);
53 }
54 
55 static ssize_t raw_image__write_sector_iov(struct disk_image *self, u64 sector, const struct iovec *iov, int iovcount)
56 {
57 	u64 offset = sector << SECTOR_SHIFT;
58 
59 	return pwritev_in_full(self->fd, iov, iovcount, offset);
60 }
61 
62 static int raw_image__read_sector_ro_mmap(struct disk_image *self, u64 sector, void *dst, u32 dst_len)
63 {
64 	u64 offset = sector << SECTOR_SHIFT;
65 
66 	if (offset + dst_len > self->size)
67 		return -1;
68 
69 	memcpy(dst, self->priv + offset, dst_len);
70 
71 	return 0;
72 }
73 
74 static int raw_image__write_sector_ro_mmap(struct disk_image *self, u64 sector, void *src, u32 src_len)
75 {
76 	u64 offset = sector << SECTOR_SHIFT;
77 
78 	if (offset + src_len > self->size)
79 		return -1;
80 
81 	memcpy(self->priv + offset, src, src_len);
82 
83 	return 0;
84 }
85 
86 static void raw_image__close_ro_mmap(struct disk_image *self)
87 {
88 	if (self->priv != MAP_FAILED)
89 		munmap(self->priv, self->size);
90 }
91 
92 static struct disk_image_operations raw_image_ops = {
93 	.read_sector_iov	= raw_image__read_sector_iov,
94 	.write_sector_iov	= raw_image__write_sector_iov
95 };
96 
97 static struct disk_image_operations raw_image_ro_mmap_ops = {
98 	.read_sector		= raw_image__read_sector_ro_mmap,
99 	.write_sector		= raw_image__write_sector_ro_mmap,
100 	.close			= raw_image__close_ro_mmap,
101 };
102 
103 static struct disk_image *raw_image__probe(int fd, struct stat *st, bool readonly)
104 {
105 	if (readonly)
106 		return disk_image__new_readonly(fd, st->st_size, &raw_image_ro_mmap_ops);
107 	else
108 		return disk_image__new(fd, st->st_size, &raw_image_ops);
109 }
110 
111 static struct disk_image *blkdev__probe(const char *filename, struct stat *st)
112 {
113 	u64 size;
114 	int fd;
115 
116 	if (!S_ISBLK(st->st_mode))
117 		return NULL;
118 
119 	fd		= open(filename, O_RDONLY);
120 	if (fd < 0)
121 		return NULL;
122 
123 	if (ioctl(fd, BLKGETSIZE64, &size) < 0) {
124 		close(fd);
125 		return NULL;
126 	}
127 
128 	return disk_image__new_readonly(fd, size, &raw_image_ro_mmap_ops);
129 }
130 
131 struct disk_image *disk_image__open(const char *filename, bool readonly)
132 {
133 	struct disk_image *self;
134 	struct stat st;
135 	int fd;
136 
137 	if (stat(filename, &st) < 0)
138 		return NULL;
139 
140 	if (S_ISBLK(st.st_mode))
141 		return blkdev__probe(filename, &st);
142 
143 	fd		= open(filename, readonly ? O_RDONLY : O_RDWR);
144 	if (fd < 0)
145 		return NULL;
146 
147 	self = qcow_probe(fd);
148 	if (self)
149 		return self;
150 
151 	self = raw_image__probe(fd, &st, readonly);
152 	if (self)
153 		return self;
154 
155 	if (close(fd) < 0)
156 		warning("close() failed");
157 
158 	return NULL;
159 }
160 
161 void disk_image__close(struct disk_image *self)
162 {
163 	/* If there was no disk image then there's nothing to do: */
164 	if (!self)
165 		return;
166 
167 	if (self->ops->close)
168 		self->ops->close(self);
169 
170 	if (close(self->fd) < 0)
171 		warning("close() failed");
172 
173 	free(self);
174 }
175