1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "qemu-common.h"
25#include "qemu/iov.h"
26#include "qemu/sockets.h"
27#include "block/coroutine.h"
28#include "migration/migration.h"
29#include "migration/qemu-file.h"
30#include "migration/qemu-file-internal.h"
31#include "trace.h"
32
33#define QSB_CHUNK_SIZE (1 << 10)
34#define QSB_MAX_CHUNK_SIZE (16 * QSB_CHUNK_SIZE)
35
36
37
38
39
40
41
42
43
44
45
46
47
48QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len)
49{
50 QEMUSizedBuffer *qsb;
51 size_t alloc_len, num_chunks, i, to_copy;
52 size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE)
53 ? QSB_MAX_CHUNK_SIZE
54 : QSB_CHUNK_SIZE;
55
56 num_chunks = DIV_ROUND_UP(len ? len : QSB_CHUNK_SIZE, chunk_size);
57 alloc_len = num_chunks * chunk_size;
58
59 qsb = g_try_new0(QEMUSizedBuffer, 1);
60 if (!qsb) {
61 return NULL;
62 }
63
64 qsb->iov = g_try_new0(struct iovec, num_chunks);
65 if (!qsb->iov) {
66 g_free(qsb);
67 return NULL;
68 }
69
70 qsb->n_iov = num_chunks;
71
72 for (i = 0; i < num_chunks; i++) {
73 qsb->iov[i].iov_base = g_try_malloc0(chunk_size);
74 if (!qsb->iov[i].iov_base) {
75
76 qsb_free(qsb);
77 return NULL;
78 }
79
80 qsb->iov[i].iov_len = chunk_size;
81 if (buffer) {
82 to_copy = (len - qsb->used) > chunk_size
83 ? chunk_size : (len - qsb->used);
84 memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy);
85 qsb->used += to_copy;
86 }
87 }
88
89 qsb->size = alloc_len;
90
91 return qsb;
92}
93
94
95
96
97
98
99void qsb_free(QEMUSizedBuffer *qsb)
100{
101 size_t i;
102
103 if (!qsb) {
104 return;
105 }
106
107 for (i = 0; i < qsb->n_iov; i++) {
108 g_free(qsb->iov[i].iov_base);
109 }
110 g_free(qsb->iov);
111 g_free(qsb);
112}
113
114
115
116
117
118
119
120
121size_t qsb_get_length(const QEMUSizedBuffer *qsb)
122{
123 return qsb->used;
124}
125
126
127
128
129
130
131
132
133
134
135
136
137
138size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len)
139{
140 if (new_len <= qsb->size) {
141 qsb->used = new_len;
142 } else {
143 qsb->used = qsb->size;
144 }
145 return qsb->used;
146}
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb,
162 off_t pos, off_t *d_off)
163{
164 ssize_t i;
165 off_t curr = 0;
166
167 if (pos > qsb->used) {
168 return -1;
169 }
170
171 for (i = 0; i < qsb->n_iov; i++) {
172 if (curr + qsb->iov[i].iov_len > pos) {
173 *d_off = pos - curr;
174 return i;
175 }
176 curr += qsb->iov[i].iov_len;
177 }
178 return -1;
179}
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start,
195 size_t count, uint8_t *buffer)
196{
197 const struct iovec *iov;
198 size_t to_copy, all_copy;
199 ssize_t index;
200 off_t s_off;
201 off_t d_off = 0;
202 char *s;
203
204 if (start > qsb->used) {
205 return 0;
206 }
207
208 all_copy = qsb->used - start;
209 if (all_copy > count) {
210 all_copy = count;
211 } else {
212 count = all_copy;
213 }
214
215 index = qsb_get_iovec(qsb, start, &s_off);
216 if (index < 0) {
217 return 0;
218 }
219
220 while (all_copy > 0) {
221 iov = &qsb->iov[index];
222
223 s = iov->iov_base;
224
225 to_copy = iov->iov_len - s_off;
226 if (to_copy > all_copy) {
227 to_copy = all_copy;
228 }
229 memcpy(&buffer[d_off], &s[s_off], to_copy);
230
231 d_off += to_copy;
232 all_copy -= to_copy;
233
234 s_off = 0;
235 index++;
236 }
237
238 return count;
239}
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size)
255{
256 size_t needed_chunks, i;
257
258 if (qsb->size < new_size) {
259 struct iovec *new_iov;
260 size_t size_diff = new_size - qsb->size;
261 size_t chunk_size = (size_diff > QSB_MAX_CHUNK_SIZE)
262 ? QSB_MAX_CHUNK_SIZE : QSB_CHUNK_SIZE;
263
264 needed_chunks = DIV_ROUND_UP(size_diff, chunk_size);
265
266 new_iov = g_try_new(struct iovec, qsb->n_iov + needed_chunks);
267 if (new_iov == NULL) {
268 return -ENOMEM;
269 }
270
271
272 for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) {
273 new_iov[i].iov_base = g_try_malloc0(chunk_size);
274 new_iov[i].iov_len = chunk_size;
275 if (!new_iov[i].iov_base) {
276 size_t j;
277
278
279 for (j = qsb->n_iov; j < i; j++) {
280 g_free(new_iov[j].iov_base);
281 }
282 g_free(new_iov);
283
284 return -ENOMEM;
285 }
286 }
287
288
289
290
291
292 for (i = 0; i < qsb->n_iov; i++) {
293 new_iov[i] = qsb->iov[i];
294 }
295
296 qsb->n_iov += needed_chunks;
297 g_free(qsb->iov);
298 qsb->iov = new_iov;
299 qsb->size += (needed_chunks * chunk_size);
300 }
301
302 return qsb->size;
303}
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source,
319 off_t pos, size_t count)
320{
321 ssize_t rc = qsb_grow(qsb, pos + count);
322 size_t to_copy;
323 size_t all_copy = count;
324 const struct iovec *iov;
325 ssize_t index;
326 char *dest;
327 off_t d_off, s_off = 0;
328
329 if (rc < 0) {
330 return rc;
331 }
332
333 if (pos + count > qsb->used) {
334 qsb->used = pos + count;
335 }
336
337 index = qsb_get_iovec(qsb, pos, &d_off);
338 if (index < 0) {
339 return -EINVAL;
340 }
341
342 while (all_copy > 0) {
343 iov = &qsb->iov[index];
344
345 dest = iov->iov_base;
346
347 to_copy = iov->iov_len - d_off;
348 if (to_copy > all_copy) {
349 to_copy = all_copy;
350 }
351
352 memcpy(&dest[d_off], &source[s_off], to_copy);
353
354 s_off += to_copy;
355 all_copy -= to_copy;
356
357 d_off = 0;
358 index++;
359 }
360
361 return count;
362}
363
364
365
366
367
368
369
370
371QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb)
372{
373 QEMUSizedBuffer *out = qsb_create(NULL, qsb_get_length(qsb));
374 size_t i;
375 ssize_t res;
376 off_t pos = 0;
377
378 if (!out) {
379 return NULL;
380 }
381
382 for (i = 0; i < qsb->n_iov; i++) {
383 res = qsb_write_at(out, qsb->iov[i].iov_base,
384 pos, qsb->iov[i].iov_len);
385 if (res < 0) {
386 qsb_free(out);
387 return NULL;
388 }
389 pos += res;
390 }
391
392 return out;
393}
394
395typedef struct QEMUBuffer {
396 QEMUSizedBuffer *qsb;
397 QEMUFile *file;
398} QEMUBuffer;
399
400static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
401{
402 QEMUBuffer *s = opaque;
403 ssize_t len = qsb_get_length(s->qsb) - pos;
404
405 if (len <= 0) {
406 return 0;
407 }
408
409 if (len > size) {
410 len = size;
411 }
412 return qsb_get_buffer(s->qsb, pos, len, buf);
413}
414
415static int buf_put_buffer(void *opaque, const uint8_t *buf,
416 int64_t pos, int size)
417{
418 QEMUBuffer *s = opaque;
419
420 return qsb_write_at(s->qsb, buf, pos, size);
421}
422
423static int buf_close(void *opaque)
424{
425 QEMUBuffer *s = opaque;
426
427 qsb_free(s->qsb);
428
429 g_free(s);
430
431 return 0;
432}
433
434const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f)
435{
436 QEMUBuffer *p;
437
438 qemu_fflush(f);
439
440 p = f->opaque;
441
442 return p->qsb;
443}
444
445static const QEMUFileOps buf_read_ops = {
446 .get_buffer = buf_get_buffer,
447 .close = buf_close,
448};
449
450static const QEMUFileOps buf_write_ops = {
451 .put_buffer = buf_put_buffer,
452 .close = buf_close,
453};
454
455QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input)
456{
457 QEMUBuffer *s;
458
459 if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') ||
460 mode[1] != '\0') {
461 error_report("qemu_bufopen: Argument validity check failed");
462 return NULL;
463 }
464
465 s = g_malloc0(sizeof(QEMUBuffer));
466 if (mode[0] == 'r') {
467 s->qsb = input;
468 }
469
470 if (s->qsb == NULL) {
471 s->qsb = qsb_create(NULL, 0);
472 }
473 if (!s->qsb) {
474 g_free(s);
475 error_report("qemu_bufopen: qsb_create failed");
476 return NULL;
477 }
478
479
480 if (mode[0] == 'r') {
481 s->file = qemu_fopen_ops(s, &buf_read_ops);
482 } else {
483 s->file = qemu_fopen_ops(s, &buf_write_ops);
484 }
485 return s->file;
486}
487