Statistics
| Branch: | Revision:

root / hw / virtio-9p.c @ 131dcb25

History | View | Annotate | Download (17.2 kB)

1
/*
2
 * Virtio 9p backend
3
 *
4
 * Copyright IBM, Corp. 2010
5
 *
6
 * Authors:
7
 *  Anthony Liguori   <aliguori@us.ibm.com>
8
 *
9
 * This work is licensed under the terms of the GNU GPL, version 2.  See
10
 * the COPYING file in the top-level directory.
11
 *
12
 */
13

    
14
#include "virtio.h"
15
#include "pc.h"
16
#include "qemu_socket.h"
17
#include "virtio-9p.h"
18
#include "fsdev/qemu-fsdev.h"
19
#include "virtio-9p-debug.h"
20

    
21
int dotu = 1;
22
int debug_9p_pdu;
23

    
24
static int v9fs_do_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf)
25
{
26
    return s->ops->lstat(&s->ctx, path->data, stbuf);
27
}
28

    
29
static int v9fs_do_setuid(V9fsState *s, uid_t uid)
30
{
31
    return s->ops->setuid(&s->ctx, uid);
32
}
33

    
34
static ssize_t v9fs_do_readlink(V9fsState *s, V9fsString *path, V9fsString *buf)
35
{
36
    ssize_t len;
37

    
38
    buf->data = qemu_malloc(1024);
39

    
40
    len = s->ops->readlink(&s->ctx, path->data, buf->data, 1024 - 1);
41
    if (len > -1) {
42
        buf->size = len;
43
        buf->data[len] = 0;
44
    }
45

    
46
    return len;
47
}
48

    
49
static int v9fs_do_close(V9fsState *s, int fd)
50
{
51
    return s->ops->close(&s->ctx, fd);
52
}
53

    
54
static int v9fs_do_closedir(V9fsState *s, DIR *dir)
55
{
56
    return s->ops->closedir(&s->ctx, dir);
57
}
58

    
59
static void v9fs_string_init(V9fsString *str)
60
{
61
    str->data = NULL;
62
    str->size = 0;
63
}
64

    
65
static void v9fs_string_free(V9fsString *str)
66
{
67
    qemu_free(str->data);
68
    str->data = NULL;
69
    str->size = 0;
70
}
71

    
72
static void v9fs_string_null(V9fsString *str)
73
{
74
    v9fs_string_free(str);
75
}
76

    
77
static int number_to_string(void *arg, char type)
78
{
79
    unsigned int ret = 0;
80

    
81
    switch (type) {
82
    case 'u': {
83
        unsigned int num = *(unsigned int *)arg;
84

    
85
        do {
86
            ret++;
87
            num = num/10;
88
        } while (num);
89
        break;
90
    }
91
    default:
92
        printf("Number_to_string: Unknown number format\n");
93
        return -1;
94
    }
95

    
96
    return ret;
97
}
98

    
99
static int v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
100
{
101
    va_list ap2;
102
    char *iter = (char *)fmt;
103
    int len = 0;
104
    int nr_args = 0;
105
    char *arg_char_ptr;
106
    unsigned int arg_uint;
107

    
108
    /* Find the number of %'s that denotes an argument */
109
    for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
110
        nr_args++;
111
        iter++;
112
    }
113

    
114
    len = strlen(fmt) - 2*nr_args;
115

    
116
    if (!nr_args) {
117
        goto alloc_print;
118
    }
119

    
120
    va_copy(ap2, ap);
121

    
122
    iter = (char *)fmt;
123

    
124
    /* Now parse the format string */
125
    for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
126
        iter++;
127
        switch (*iter) {
128
        case 'u':
129
            arg_uint = va_arg(ap2, unsigned int);
130
            len += number_to_string((void *)&arg_uint, 'u');
131
            break;
132
        case 's':
133
            arg_char_ptr = va_arg(ap2, char *);
134
            len += strlen(arg_char_ptr);
135
            break;
136
        case 'c':
137
            len += 1;
138
            break;
139
        default:
140
            fprintf(stderr,
141
                    "v9fs_string_alloc_printf:Incorrect format %c", *iter);
142
            return -1;
143
        }
144
        iter++;
145
    }
146

    
147
alloc_print:
148
    *strp = qemu_malloc((len + 1) * sizeof(**strp));
149

    
150
    return vsprintf(*strp, fmt, ap);
151
}
152

    
153
static void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
154
{
155
    va_list ap;
156
    int err;
157

    
158
    v9fs_string_free(str);
159

    
160
    va_start(ap, fmt);
161
    err = v9fs_string_alloc_printf(&str->data, fmt, ap);
162
    BUG_ON(err == -1);
163
    va_end(ap);
164

    
165
    str->size = err;
166
}
167

    
168
static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
169
{
170
    v9fs_string_free(lhs);
171
    v9fs_string_sprintf(lhs, "%s", rhs->data);
172
}
173

    
174
static size_t v9fs_string_size(V9fsString *str)
175
{
176
    return str->size;
177
}
178

    
179
static V9fsPDU *alloc_pdu(V9fsState *s)
180
{
181
    V9fsPDU *pdu = NULL;
182

    
183
    if (!QLIST_EMPTY(&s->free_list)) {
184
        pdu = QLIST_FIRST(&s->free_list);
185
        QLIST_REMOVE(pdu, next);
186
    }
187
    return pdu;
188
}
189

    
190
static void free_pdu(V9fsState *s, V9fsPDU *pdu)
191
{
192
    if (pdu) {
193
        QLIST_INSERT_HEAD(&s->free_list, pdu, next);
194
    }
195
}
196

    
197
size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
198
                        size_t offset, size_t size, int pack)
199
{
200
    int i = 0;
201
    size_t copied = 0;
202

    
203
    for (i = 0; size && i < sg_count; i++) {
204
        size_t len;
205
        if (offset >= sg[i].iov_len) {
206
            /* skip this sg */
207
            offset -= sg[i].iov_len;
208
            continue;
209
        } else {
210
            len = MIN(sg[i].iov_len - offset, size);
211
            if (pack) {
212
                memcpy(sg[i].iov_base + offset, addr, len);
213
            } else {
214
                memcpy(addr, sg[i].iov_base + offset, len);
215
            }
216
            size -= len;
217
            copied += len;
218
            addr += len;
219
            if (size) {
220
                offset = 0;
221
                continue;
222
            }
223
        }
224
    }
225

    
226
    return copied;
227
}
228

    
229
static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
230
{
231
    return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num,
232
                         offset, size, 0);
233
}
234

    
235
static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
236
                        size_t size)
237
{
238
    return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num,
239
                             offset, size, 1);
240
}
241

    
242
static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg)
243
{
244
    size_t pos = 0;
245
    int i, j;
246
    struct iovec *src_sg;
247
    unsigned int num;
248

    
249
    if (rx) {
250
        src_sg = pdu->elem.in_sg;
251
        num = pdu->elem.in_num;
252
    } else {
253
        src_sg = pdu->elem.out_sg;
254
        num = pdu->elem.out_num;
255
    }
256

    
257
    j = 0;
258
    for (i = 0; i < num; i++) {
259
        if (offset <= pos) {
260
            sg[j].iov_base = src_sg[i].iov_base;
261
            sg[j].iov_len = src_sg[i].iov_len;
262
            j++;
263
        } else if (offset < (src_sg[i].iov_len + pos)) {
264
            sg[j].iov_base = src_sg[i].iov_base;
265
            sg[j].iov_len = src_sg[i].iov_len;
266
            sg[j].iov_base += (offset - pos);
267
            sg[j].iov_len -= (offset - pos);
268
            j++;
269
        }
270
        pos += src_sg[i].iov_len;
271
    }
272

    
273
    return j;
274
}
275

    
276
static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
277
{
278
    size_t old_offset = offset;
279
    va_list ap;
280
    int i;
281

    
282
    va_start(ap, fmt);
283
    for (i = 0; fmt[i]; i++) {
284
        switch (fmt[i]) {
285
        case 'b': {
286
            uint8_t *valp = va_arg(ap, uint8_t *);
287
            offset += pdu_unpack(valp, pdu, offset, sizeof(*valp));
288
            break;
289
        }
290
        case 'w': {
291
            uint16_t val, *valp;
292
            valp = va_arg(ap, uint16_t *);
293
            val = le16_to_cpupu(valp);
294
            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
295
            *valp = val;
296
            break;
297
        }
298
        case 'd': {
299
            uint32_t val, *valp;
300
            valp = va_arg(ap, uint32_t *);
301
            val = le32_to_cpupu(valp);
302
            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
303
            *valp = val;
304
            break;
305
        }
306
        case 'q': {
307
            uint64_t val, *valp;
308
            valp = va_arg(ap, uint64_t *);
309
            val = le64_to_cpup(valp);
310
            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
311
            *valp = val;
312
            break;
313
        }
314
        case 'v': {
315
            struct iovec *iov = va_arg(ap, struct iovec *);
316
            int *iovcnt = va_arg(ap, int *);
317
            *iovcnt = pdu_copy_sg(pdu, offset, 0, iov);
318
            break;
319
        }
320
        case 's': {
321
            V9fsString *str = va_arg(ap, V9fsString *);
322
            offset += pdu_unmarshal(pdu, offset, "w", &str->size);
323
            /* FIXME: sanity check str->size */
324
            str->data = qemu_malloc(str->size + 1);
325
            offset += pdu_unpack(str->data, pdu, offset, str->size);
326
            str->data[str->size] = 0;
327
            break;
328
        }
329
        case 'Q': {
330
            V9fsQID *qidp = va_arg(ap, V9fsQID *);
331
            offset += pdu_unmarshal(pdu, offset, "bdq",
332
                        &qidp->type, &qidp->version, &qidp->path);
333
            break;
334
        }
335
        case 'S': {
336
            V9fsStat *statp = va_arg(ap, V9fsStat *);
337
            offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd",
338
                        &statp->size, &statp->type, &statp->dev,
339
                        &statp->qid, &statp->mode, &statp->atime,
340
                        &statp->mtime, &statp->length,
341
                        &statp->name, &statp->uid, &statp->gid,
342
                        &statp->muid, &statp->extension,
343
                        &statp->n_uid, &statp->n_gid,
344
                        &statp->n_muid);
345
            break;
346
        }
347
        default:
348
            break;
349
        }
350
    }
351

    
352
    va_end(ap);
353

    
354
    return offset - old_offset;
355
}
356

    
357
static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
358
{
359
    size_t old_offset = offset;
360
    va_list ap;
361
    int i;
362

    
363
    va_start(ap, fmt);
364
    for (i = 0; fmt[i]; i++) {
365
        switch (fmt[i]) {
366
        case 'b': {
367
            uint8_t val = va_arg(ap, int);
368
            offset += pdu_pack(pdu, offset, &val, sizeof(val));
369
            break;
370
        }
371
        case 'w': {
372
            uint16_t val;
373
            cpu_to_le16w(&val, va_arg(ap, int));
374
            offset += pdu_pack(pdu, offset, &val, sizeof(val));
375
            break;
376
        }
377
        case 'd': {
378
            uint32_t val;
379
            cpu_to_le32w(&val, va_arg(ap, uint32_t));
380
            offset += pdu_pack(pdu, offset, &val, sizeof(val));
381
            break;
382
        }
383
        case 'q': {
384
            uint64_t val;
385
            cpu_to_le64w(&val, va_arg(ap, uint64_t));
386
            offset += pdu_pack(pdu, offset, &val, sizeof(val));
387
            break;
388
        }
389
        case 'v': {
390
            struct iovec *iov = va_arg(ap, struct iovec *);
391
            int *iovcnt = va_arg(ap, int *);
392
            *iovcnt = pdu_copy_sg(pdu, offset, 1, iov);
393
            break;
394
        }
395
        case 's': {
396
            V9fsString *str = va_arg(ap, V9fsString *);
397
            offset += pdu_marshal(pdu, offset, "w", str->size);
398
            offset += pdu_pack(pdu, offset, str->data, str->size);
399
            break;
400
        }
401
        case 'Q': {
402
            V9fsQID *qidp = va_arg(ap, V9fsQID *);
403
            offset += pdu_marshal(pdu, offset, "bdq",
404
                        qidp->type, qidp->version, qidp->path);
405
            break;
406
        }
407
        case 'S': {
408
            V9fsStat *statp = va_arg(ap, V9fsStat *);
409
            offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd",
410
                        statp->size, statp->type, statp->dev,
411
                        &statp->qid, statp->mode, statp->atime,
412
                        statp->mtime, statp->length, &statp->name,
413
                        &statp->uid, &statp->gid, &statp->muid,
414
                        &statp->extension, statp->n_uid,
415
                        statp->n_gid, statp->n_muid);
416
            break;
417
        }
418
        default:
419
            break;
420
        }
421
    }
422
    va_end(ap);
423

    
424
    return offset - old_offset;
425
}
426

    
427
static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
428
{
429
    int8_t id = pdu->id + 1; /* Response */
430

    
431
    if (len < 0) {
432
        V9fsString str;
433
        int err = -len;
434

    
435
        str.data = strerror(err);
436
        str.size = strlen(str.data);
437

    
438
        len = 7;
439
        len += pdu_marshal(pdu, len, "s", &str);
440
        if (dotu) {
441
            len += pdu_marshal(pdu, len, "d", err);
442
        }
443

    
444
        id = P9_RERROR;
445
    }
446

    
447
    /* fill out the header */
448
    pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
449

    
450
    /* keep these in sync */
451
    pdu->size = len;
452
    pdu->id = id;
453

    
454
    /* push onto queue and notify */
455
    virtqueue_push(s->vq, &pdu->elem, len);
456

    
457
    /* FIXME: we should batch these completions */
458
    virtio_notify(&s->vdev, s->vq);
459

    
460
    free_pdu(s, pdu);
461
}
462

    
463
static void v9fs_dummy(V9fsState *s, V9fsPDU *pdu)
464
{
465
    /* Note: The following have been added to prevent GCC from complaining
466
     * They will be removed in the subsequent patches */
467
    (void)pdu_unmarshal;
468
    (void) complete_pdu;
469
    (void) v9fs_string_init;
470
    (void) v9fs_string_free;
471
    (void) v9fs_string_null;
472
    (void) v9fs_string_sprintf;
473
    (void) v9fs_string_copy;
474
    (void) v9fs_string_size;
475
    (void) v9fs_do_lstat;
476
    (void) v9fs_do_setuid;
477
    (void) v9fs_do_readlink;
478
    (void) v9fs_do_close;
479
    (void) v9fs_do_closedir;
480
}
481

    
482
static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
483
{
484
    if (debug_9p_pdu) {
485
        pprint_pdu(pdu);
486
    }
487
}
488

    
489
static void v9fs_attach(V9fsState *s, V9fsPDU *pdu)
490
{
491
    if (debug_9p_pdu) {
492
        pprint_pdu(pdu);
493
    }
494
}
495

    
496
static void v9fs_stat(V9fsState *s, V9fsPDU *pdu)
497
{
498
    if (debug_9p_pdu) {
499
        pprint_pdu(pdu);
500
    }
501
}
502

    
503
static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
504
{
505
    if (debug_9p_pdu) {
506
        pprint_pdu(pdu);
507
    }
508
}
509

    
510
static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
511
{
512
    if (debug_9p_pdu) {
513
        pprint_pdu(pdu);
514
    }
515
}
516

    
517
static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
518
{    if (debug_9p_pdu) {
519
        pprint_pdu(pdu);
520
     }
521
}
522

    
523
static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
524
{
525
    if (debug_9p_pdu) {
526
        pprint_pdu(pdu);
527
    }
528
}
529

    
530
static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
531
{
532
    if (debug_9p_pdu) {
533
        pprint_pdu(pdu);
534
    }
535
}
536

    
537
static void v9fs_create(V9fsState *s, V9fsPDU *pdu)
538
{
539
    if (debug_9p_pdu) {
540
        pprint_pdu(pdu);
541
    }
542
}
543

    
544
static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
545
{
546
    v9fs_dummy(s, pdu);
547
    if (debug_9p_pdu) {
548
        pprint_pdu(pdu);
549
    }
550
}
551

    
552
static void v9fs_remove(V9fsState *s, V9fsPDU *pdu)
553
{
554
    if (debug_9p_pdu) {
555
        pprint_pdu(pdu);
556
    }
557
}
558

    
559
static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
560
{
561
    if (debug_9p_pdu) {
562
        pprint_pdu(pdu);
563
    }
564
}
565

    
566
typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
567

    
568
static pdu_handler_t *pdu_handlers[] = {
569
    [P9_TVERSION] = v9fs_version,
570
    [P9_TATTACH] = v9fs_attach,
571
    [P9_TSTAT] = v9fs_stat,
572
    [P9_TWALK] = v9fs_walk,
573
    [P9_TCLUNK] = v9fs_clunk,
574
    [P9_TOPEN] = v9fs_open,
575
    [P9_TREAD] = v9fs_read,
576
#if 0
577
    [P9_TAUTH] = v9fs_auth,
578
#endif
579
    [P9_TFLUSH] = v9fs_flush,
580
    [P9_TCREATE] = v9fs_create,
581
    [P9_TWRITE] = v9fs_write,
582
    [P9_TWSTAT] = v9fs_wstat,
583
    [P9_TREMOVE] = v9fs_remove,
584
};
585

    
586
static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
587
{
588
    pdu_handler_t *handler;
589

    
590
    if (debug_9p_pdu) {
591
        pprint_pdu(pdu);
592
    }
593

    
594
    BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers));
595

    
596
    handler = pdu_handlers[pdu->id];
597
    BUG_ON(handler == NULL);
598

    
599
    handler(s, pdu);
600
}
601

    
602
static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
603
{
604
    V9fsState *s = (V9fsState *)vdev;
605
    V9fsPDU *pdu;
606
    ssize_t len;
607

    
608
    while ((pdu = alloc_pdu(s)) &&
609
            (len = virtqueue_pop(vq, &pdu->elem)) != 0) {
610
        uint8_t *ptr;
611

    
612
        BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
613
        BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
614

    
615
        ptr = pdu->elem.out_sg[0].iov_base;
616

    
617
        memcpy(&pdu->size, ptr, 4);
618
        pdu->id = ptr[4];
619
        memcpy(&pdu->tag, ptr + 5, 2);
620

    
621
        submit_pdu(s, pdu);
622
    }
623

    
624
    free_pdu(s, pdu);
625
}
626

    
627
static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
628
{
629
    features |= 1 << VIRTIO_9P_MOUNT_TAG;
630
    return features;
631
}
632

    
633
static V9fsState *to_virtio_9p(VirtIODevice *vdev)
634
{
635
    return (V9fsState *)vdev;
636
}
637

    
638
static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
639
{
640
    struct virtio_9p_config *cfg;
641
    V9fsState *s = to_virtio_9p(vdev);
642

    
643
    cfg = qemu_mallocz(sizeof(struct virtio_9p_config) +
644
                        s->tag_len);
645
    stw_raw(&cfg->tag_len, s->tag_len);
646
    memcpy(cfg->tag, s->tag, s->tag_len);
647
    memcpy(config, cfg, s->config_size);
648
    qemu_free(cfg);
649
}
650

    
651
VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
652
 {
653
    V9fsState *s;
654
    int i, len;
655
    struct stat stat;
656
    FsTypeEntry *fse;
657

    
658

    
659
    s = (V9fsState *)virtio_common_init("virtio-9p",
660
                                    VIRTIO_ID_9P,
661
                                    sizeof(struct virtio_9p_config)+
662
                                    MAX_TAG_LEN,
663
                                    sizeof(V9fsState));
664

    
665
    /* initialize pdu allocator */
666
    QLIST_INIT(&s->free_list);
667
    for (i = 0; i < (MAX_REQ - 1); i++) {
668
        QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
669
    }
670

    
671
    s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output);
672

    
673
    fse = get_fsdev_fsentry(conf->fsdev_id);
674

    
675
    if (!fse) {
676
        /* We don't have a fsdev identified by fsdev_id */
677
        fprintf(stderr, "Virtio-9p device couldn't find fsdev "
678
                    "with the id %s\n", conf->fsdev_id);
679
        exit(1);
680
    }
681

    
682
    if (!fse->path || !conf->tag) {
683
        /* we haven't specified a mount_tag or the path */
684
        fprintf(stderr, "fsdev with id %s needs path "
685
                "and Virtio-9p device needs mount_tag arguments\n",
686
                conf->fsdev_id);
687
        exit(1);
688
    }
689

    
690
    if (lstat(fse->path, &stat)) {
691
        fprintf(stderr, "share path %s does not exist\n", fse->path);
692
        exit(1);
693
    } else if (!S_ISDIR(stat.st_mode)) {
694
        fprintf(stderr, "share path %s is not a directory \n", fse->path);
695
        exit(1);
696
    }
697

    
698
    s->ctx.fs_root = qemu_strdup(fse->path);
699
    len = strlen(conf->tag);
700
    if (len > MAX_TAG_LEN) {
701
        len = MAX_TAG_LEN;
702
    }
703
    /* s->tag is non-NULL terminated string */
704
    s->tag = qemu_malloc(len);
705
    memcpy(s->tag, conf->tag, len);
706
    s->tag_len = len;
707
    s->ctx.uid = -1;
708

    
709
    s->ops = fse->ops;
710
    s->vdev.get_features = virtio_9p_get_features;
711
    s->config_size = sizeof(struct virtio_9p_config) +
712
                        s->tag_len;
713
    s->vdev.get_config = virtio_9p_get_config;
714

    
715
    return &s->vdev;
716
}