xref: /kvmtool/virtio/9p-pdu.c (revision c6cb7c757339c7f465934dbb81fa01f1659114d5)
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