1 #include "kvm/util.h"
2 #include "kvm/virtio-9p.h"
3
4 #include <endian.h>
5 #include <stdint.h>
6
7 #include <linux/compiler.h>
8 #include <linux/9p.h>
9
virtio_p9_pdu_read(struct p9_pdu * pdu,void * data,size_t size)10 static void virtio_p9_pdu_read(struct p9_pdu *pdu, void *data, size_t size)
11 {
12 size_t len;
13 int i, copied = 0;
14 u16 iov_cnt = pdu->out_iov_cnt;
15 size_t offset = pdu->read_offset;
16 struct iovec *iov = pdu->out_iov;
17
18 for (i = 0; i < iov_cnt && size; i++) {
19 if (offset >= iov[i].iov_len) {
20 offset -= iov[i].iov_len;
21 continue;
22 } else {
23 len = MIN(iov[i].iov_len - offset, size);
24 memcpy(data, iov[i].iov_base + offset, len);
25 size -= len;
26 data += len;
27 offset = 0;
28 copied += len;
29 }
30 }
31 pdu->read_offset += copied;
32 }
33
virtio_p9_pdu_write(struct p9_pdu * pdu,const void * data,size_t size)34 static void virtio_p9_pdu_write(struct p9_pdu *pdu,
35 const void *data, size_t size)
36 {
37 size_t len;
38 int i, copied = 0;
39 u16 iov_cnt = pdu->in_iov_cnt;
40 size_t offset = pdu->write_offset;
41 struct iovec *iov = pdu->in_iov;
42
43 for (i = 0; i < iov_cnt && size; i++) {
44 if (offset >= iov[i].iov_len) {
45 offset -= iov[i].iov_len;
46 continue;
47 } else {
48 len = MIN(iov[i].iov_len - offset, size);
49 memcpy(iov[i].iov_base + offset, data, len);
50 size -= len;
51 data += len;
52 offset = 0;
53 copied += len;
54 }
55 }
56 pdu->write_offset += copied;
57 }
58
virtio_p9_wstat_free(struct p9_wstat * stbuf)59 static void virtio_p9_wstat_free(struct p9_wstat *stbuf)
60 {
61 free(stbuf->name);
62 free(stbuf->uid);
63 free(stbuf->gid);
64 free(stbuf->muid);
65 }
66
virtio_p9_decode(struct p9_pdu * pdu,const char * fmt,va_list ap)67 static int virtio_p9_decode(struct p9_pdu *pdu, const char *fmt, va_list ap)
68 {
69 int retval = 0;
70 const char *ptr;
71
72 for (ptr = fmt; *ptr; ptr++) {
73 switch (*ptr) {
74 case 'b':
75 {
76 int8_t *val = va_arg(ap, int8_t *);
77 virtio_p9_pdu_read(pdu, val, sizeof(*val));
78 }
79 break;
80 case 'w':
81 {
82 int16_t le_val;
83 int16_t *val = va_arg(ap, int16_t *);
84 virtio_p9_pdu_read(pdu, &le_val, sizeof(le_val));
85 *val = le16toh(le_val);
86 }
87 break;
88 case 'd':
89 {
90 int32_t le_val;
91 int32_t *val = va_arg(ap, int32_t *);
92 virtio_p9_pdu_read(pdu, &le_val, sizeof(le_val));
93 *val = le32toh(le_val);
94 }
95 break;
96 case 'q':
97 {
98 int64_t le_val;
99 int64_t *val = va_arg(ap, int64_t *);
100 virtio_p9_pdu_read(pdu, &le_val, sizeof(le_val));
101 *val = le64toh(le_val);
102 }
103 break;
104 case 's':
105 {
106 int16_t len;
107 char **str = va_arg(ap, char **);
108
109 virtio_p9_pdu_readf(pdu, "w", &len);
110 *str = malloc(len + 1);
111 if (*str == NULL) {
112 retval = ENOMEM;
113 break;
114 }
115 virtio_p9_pdu_read(pdu, *str, len);
116 (*str)[len] = 0;
117 }
118 break;
119 case 'Q':
120 {
121 struct p9_qid *qid = va_arg(ap, struct p9_qid *);
122 retval = virtio_p9_pdu_readf(pdu, "bdq",
123 &qid->type, &qid->version,
124 &qid->path);
125 }
126 break;
127 case 'S':
128 {
129 struct p9_wstat *stbuf = va_arg(ap, struct p9_wstat *);
130 memset(stbuf, 0, sizeof(struct p9_wstat));
131 stbuf->n_uid = KUIDT_INIT(-1);
132 stbuf->n_gid = KGIDT_INIT(-1);
133 stbuf->n_muid = KUIDT_INIT(-1);
134 retval = virtio_p9_pdu_readf(pdu, "wwdQdddqssss",
135 &stbuf->size, &stbuf->type,
136 &stbuf->dev, &stbuf->qid,
137 &stbuf->mode, &stbuf->atime,
138 &stbuf->mtime, &stbuf->length,
139 &stbuf->name, &stbuf->uid,
140 &stbuf->gid, &stbuf->muid);
141 if (retval)
142 virtio_p9_wstat_free(stbuf);
143 }
144 break;
145 case 'I':
146 {
147 struct p9_iattr_dotl *p9attr = va_arg(ap,
148 struct p9_iattr_dotl *);
149
150 retval = virtio_p9_pdu_readf(pdu, "ddddqqqqq",
151 &p9attr->valid,
152 &p9attr->mode,
153 &p9attr->uid,
154 &p9attr->gid,
155 &p9attr->size,
156 &p9attr->atime_sec,
157 &p9attr->atime_nsec,
158 &p9attr->mtime_sec,
159 &p9attr->mtime_nsec);
160 }
161 break;
162 default:
163 retval = EINVAL;
164 break;
165 }
166 }
167 return retval;
168 }
169
virtio_p9_pdu_encode(struct p9_pdu * pdu,const char * fmt,va_list ap)170 static int virtio_p9_pdu_encode(struct p9_pdu *pdu, const char *fmt, va_list ap)
171 {
172 int retval = 0;
173 const char *ptr;
174
175 for (ptr = fmt; *ptr; ptr++) {
176 switch (*ptr) {
177 case 'b':
178 {
179 int8_t val = va_arg(ap, int);
180 virtio_p9_pdu_write(pdu, &val, sizeof(val));
181 }
182 break;
183 case 'w':
184 {
185 int16_t val = htole16(va_arg(ap, int));
186 virtio_p9_pdu_write(pdu, &val, sizeof(val));
187 }
188 break;
189 case 'd':
190 {
191 int32_t val = htole32(va_arg(ap, int32_t));
192 virtio_p9_pdu_write(pdu, &val, sizeof(val));
193 }
194 break;
195 case 'q':
196 {
197 int64_t val = htole64(va_arg(ap, int64_t));
198 virtio_p9_pdu_write(pdu, &val, sizeof(val));
199 }
200 break;
201 case 's':
202 {
203 uint16_t len = 0;
204 const char *s = va_arg(ap, char *);
205 if (s)
206 len = MIN(strlen(s), USHRT_MAX);
207 virtio_p9_pdu_writef(pdu, "w", len);
208 virtio_p9_pdu_write(pdu, s, len);
209 }
210 break;
211 case 'Q':
212 {
213 struct p9_qid *qid = va_arg(ap, struct p9_qid *);
214 retval = virtio_p9_pdu_writef(pdu, "bdq",
215 qid->type, qid->version,
216 qid->path);
217 }
218 break;
219 case 'S':
220 {
221 struct p9_wstat *stbuf = va_arg(ap, struct p9_wstat *);
222 retval = virtio_p9_pdu_writef(pdu, "wwdQdddqssss",
223 stbuf->size, stbuf->type,
224 stbuf->dev, &stbuf->qid,
225 stbuf->mode, stbuf->atime,
226 stbuf->mtime, stbuf->length,
227 stbuf->name, stbuf->uid,
228 stbuf->gid, stbuf->muid);
229 }
230 break;
231 case 'A':
232 {
233 struct p9_stat_dotl *stbuf = va_arg(ap,
234 struct p9_stat_dotl *);
235 retval = virtio_p9_pdu_writef(pdu,
236 "qQdddqqqqqqqqqqqqqqq",
237 stbuf->st_result_mask,
238 &stbuf->qid,
239 stbuf->st_mode,
240 stbuf->st_uid,
241 stbuf->st_gid,
242 stbuf->st_nlink,
243 stbuf->st_rdev,
244 stbuf->st_size,
245 stbuf->st_blksize,
246 stbuf->st_blocks,
247 stbuf->st_atime_sec,
248 stbuf->st_atime_nsec,
249 stbuf->st_mtime_sec,
250 stbuf->st_mtime_nsec,
251 stbuf->st_ctime_sec,
252 stbuf->st_ctime_nsec,
253 stbuf->st_btime_sec,
254 stbuf->st_btime_nsec,
255 stbuf->st_gen,
256 stbuf->st_data_version);
257 }
258 break;
259 default:
260 retval = EINVAL;
261 break;
262 }
263 }
264 return retval;
265 }
266
virtio_p9_pdu_readf(struct p9_pdu * pdu,const char * fmt,...)267 int virtio_p9_pdu_readf(struct p9_pdu *pdu, const char *fmt, ...)
268 {
269 int ret;
270 va_list ap;
271
272 va_start(ap, fmt);
273 ret = virtio_p9_decode(pdu, fmt, ap);
274 va_end(ap);
275
276 return ret;
277 }
278
virtio_p9_pdu_writef(struct p9_pdu * pdu,const char * fmt,...)279 int virtio_p9_pdu_writef(struct p9_pdu *pdu, const char *fmt, ...)
280 {
281 int ret;
282 va_list ap;
283
284 va_start(ap, fmt);
285 ret = virtio_p9_pdu_encode(pdu, fmt, ap);
286 va_end(ap);
287
288 return ret;
289 }
290