]> git.proxmox.com Git - mirror_qemu.git/blame - hw/virtio-9p.c
virtio-9p: Add P9_TWRITE support
[mirror_qemu.git] / hw / virtio-9p.c
CommitLineData
9f107513
AL
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
21int dotu = 1;
22int debug_9p_pdu;
23
131dcb25
AL
24static int v9fs_do_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf)
25{
26 return s->ops->lstat(&s->ctx, path->data, stbuf);
27}
28
29static int v9fs_do_setuid(V9fsState *s, uid_t uid)
30{
31 return s->ops->setuid(&s->ctx, uid);
32}
33
34static 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
49static int v9fs_do_close(V9fsState *s, int fd)
50{
51 return s->ops->close(&s->ctx, fd);
52}
53
54static int v9fs_do_closedir(V9fsState *s, DIR *dir)
55{
56 return s->ops->closedir(&s->ctx, dir);
57}
58
a6568fe2
AL
59static int v9fs_do_open(V9fsState *s, V9fsString *path, int flags)
60{
61 return s->ops->open(&s->ctx, path->data, flags);
62}
63
64static DIR *v9fs_do_opendir(V9fsState *s, V9fsString *path)
65{
66 return s->ops->opendir(&s->ctx, path->data);
67}
68
a9231555
AL
69static void v9fs_do_rewinddir(V9fsState *s, DIR *dir)
70{
71 return s->ops->rewinddir(&s->ctx, dir);
72}
73
74static off_t v9fs_do_telldir(V9fsState *s, DIR *dir)
75{
76 return s->ops->telldir(&s->ctx, dir);
77}
78
79static struct dirent *v9fs_do_readdir(V9fsState *s, DIR *dir)
80{
81 return s->ops->readdir(&s->ctx, dir);
82}
83
84static void v9fs_do_seekdir(V9fsState *s, DIR *dir, off_t off)
85{
86 return s->ops->seekdir(&s->ctx, dir, off);
87}
88
89static int v9fs_do_readv(V9fsState *s, int fd, const struct iovec *iov,
90 int iovcnt)
91{
92 return s->ops->readv(&s->ctx, fd, iov, iovcnt);
93}
94
95static off_t v9fs_do_lseek(V9fsState *s, int fd, off_t offset, int whence)
96{
97 return s->ops->lseek(&s->ctx, fd, offset, whence);
98}
99
8449360c
AL
100static int v9fs_do_writev(V9fsState *s, int fd, const struct iovec *iov,
101 int iovcnt)
102{
103 return s->ops->writev(&s->ctx, fd, iov, iovcnt);
104}
105
a03f7874
AL
106static void v9fs_string_init(V9fsString *str)
107{
108 str->data = NULL;
109 str->size = 0;
110}
111
112static void v9fs_string_free(V9fsString *str)
113{
114 qemu_free(str->data);
115 str->data = NULL;
116 str->size = 0;
117}
118
119static void v9fs_string_null(V9fsString *str)
120{
121 v9fs_string_free(str);
122}
123
124static int number_to_string(void *arg, char type)
125{
126 unsigned int ret = 0;
127
128 switch (type) {
129 case 'u': {
130 unsigned int num = *(unsigned int *)arg;
131
132 do {
133 ret++;
134 num = num/10;
135 } while (num);
136 break;
137 }
138 default:
139 printf("Number_to_string: Unknown number format\n");
140 return -1;
141 }
142
143 return ret;
144}
145
146static int v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
147{
148 va_list ap2;
149 char *iter = (char *)fmt;
150 int len = 0;
151 int nr_args = 0;
152 char *arg_char_ptr;
153 unsigned int arg_uint;
154
155 /* Find the number of %'s that denotes an argument */
156 for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
157 nr_args++;
158 iter++;
159 }
160
161 len = strlen(fmt) - 2*nr_args;
162
163 if (!nr_args) {
164 goto alloc_print;
165 }
166
167 va_copy(ap2, ap);
168
169 iter = (char *)fmt;
170
171 /* Now parse the format string */
172 for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
173 iter++;
174 switch (*iter) {
175 case 'u':
176 arg_uint = va_arg(ap2, unsigned int);
177 len += number_to_string((void *)&arg_uint, 'u');
178 break;
179 case 's':
180 arg_char_ptr = va_arg(ap2, char *);
181 len += strlen(arg_char_ptr);
182 break;
183 case 'c':
184 len += 1;
185 break;
186 default:
187 fprintf(stderr,
188 "v9fs_string_alloc_printf:Incorrect format %c", *iter);
189 return -1;
190 }
191 iter++;
192 }
193
194alloc_print:
195 *strp = qemu_malloc((len + 1) * sizeof(**strp));
196
197 return vsprintf(*strp, fmt, ap);
198}
199
200static void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
201{
202 va_list ap;
203 int err;
204
205 v9fs_string_free(str);
206
207 va_start(ap, fmt);
208 err = v9fs_string_alloc_printf(&str->data, fmt, ap);
209 BUG_ON(err == -1);
210 va_end(ap);
211
212 str->size = err;
213}
214
215static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
216{
217 v9fs_string_free(lhs);
218 v9fs_string_sprintf(lhs, "%s", rhs->data);
219}
220
221static size_t v9fs_string_size(V9fsString *str)
222{
223 return str->size;
224}
225
286d5652
AL
226static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid)
227{
228 V9fsFidState *f;
229
230 for (f = s->fid_list; f; f = f->next) {
231 if (f->fid == fid) {
232 v9fs_do_setuid(s, f->uid);
233 return f;
234 }
235 }
236
237 return NULL;
238}
239
240static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
241{
242 V9fsFidState *f;
243
244 f = lookup_fid(s, fid);
245 if (f) {
246 return NULL;
247 }
248
249 f = qemu_mallocz(sizeof(V9fsFidState));
250
251 f->fid = fid;
252 f->fd = -1;
253 f->dir = NULL;
254
255 f->next = s->fid_list;
256 s->fid_list = f;
257
258 return f;
259}
260
261static int free_fid(V9fsState *s, int32_t fid)
262{
263 V9fsFidState **fidpp, *fidp;
264
265 for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
266 if ((*fidpp)->fid == fid) {
267 break;
268 }
269 }
270
271 if (*fidpp == NULL) {
272 return -ENOENT;
273 }
274
275 fidp = *fidpp;
276 *fidpp = fidp->next;
277
278 if (fidp->fd != -1) {
279 v9fs_do_close(s, fidp->fd);
280 }
281 if (fidp->dir) {
282 v9fs_do_closedir(s, fidp->dir);
283 }
284 v9fs_string_free(&fidp->path);
285 qemu_free(fidp);
286
287 return 0;
288}
289
290#define P9_QID_TYPE_DIR 0x80
291#define P9_QID_TYPE_SYMLINK 0x02
292
293#define P9_STAT_MODE_DIR 0x80000000
294#define P9_STAT_MODE_APPEND 0x40000000
295#define P9_STAT_MODE_EXCL 0x20000000
296#define P9_STAT_MODE_MOUNT 0x10000000
297#define P9_STAT_MODE_AUTH 0x08000000
298#define P9_STAT_MODE_TMP 0x04000000
299#define P9_STAT_MODE_SYMLINK 0x02000000
300#define P9_STAT_MODE_LINK 0x01000000
301#define P9_STAT_MODE_DEVICE 0x00800000
302#define P9_STAT_MODE_NAMED_PIPE 0x00200000
303#define P9_STAT_MODE_SOCKET 0x00100000
304#define P9_STAT_MODE_SETUID 0x00080000
305#define P9_STAT_MODE_SETGID 0x00040000
306#define P9_STAT_MODE_SETVTX 0x00010000
307
308#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR | \
309 P9_STAT_MODE_SYMLINK | \
310 P9_STAT_MODE_LINK | \
311 P9_STAT_MODE_DEVICE | \
312 P9_STAT_MODE_NAMED_PIPE | \
313 P9_STAT_MODE_SOCKET)
314
315/* This is the algorithm from ufs in spfs */
316static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
317{
318 size_t size;
319
320 size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
321 memcpy(&qidp->path, &stbuf->st_ino, size);
322 qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
323 qidp->type = 0;
324 if (S_ISDIR(stbuf->st_mode)) {
325 qidp->type |= P9_QID_TYPE_DIR;
326 }
327 if (S_ISLNK(stbuf->st_mode)) {
328 qidp->type |= P9_QID_TYPE_SYMLINK;
329 }
330}
331
332static int fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp)
333{
334 struct stat stbuf;
335 int err;
336
337 err = v9fs_do_lstat(s, &fidp->path, &stbuf);
338 if (err) {
339 return err;
340 }
341
342 stat_to_qid(&stbuf, qidp);
343 return 0;
344}
345
9f107513
AL
346static V9fsPDU *alloc_pdu(V9fsState *s)
347{
348 V9fsPDU *pdu = NULL;
349
350 if (!QLIST_EMPTY(&s->free_list)) {
351 pdu = QLIST_FIRST(&s->free_list);
352 QLIST_REMOVE(pdu, next);
353 }
354 return pdu;
355}
356
357static void free_pdu(V9fsState *s, V9fsPDU *pdu)
358{
359 if (pdu) {
360 QLIST_INSERT_HEAD(&s->free_list, pdu, next);
361 }
362}
363
364size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
365 size_t offset, size_t size, int pack)
366{
367 int i = 0;
368 size_t copied = 0;
369
370 for (i = 0; size && i < sg_count; i++) {
371 size_t len;
372 if (offset >= sg[i].iov_len) {
373 /* skip this sg */
374 offset -= sg[i].iov_len;
375 continue;
376 } else {
377 len = MIN(sg[i].iov_len - offset, size);
378 if (pack) {
379 memcpy(sg[i].iov_base + offset, addr, len);
380 } else {
381 memcpy(addr, sg[i].iov_base + offset, len);
382 }
383 size -= len;
384 copied += len;
385 addr += len;
386 if (size) {
387 offset = 0;
388 continue;
389 }
390 }
391 }
392
393 return copied;
394}
395
405a549a
AL
396static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
397{
398 return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num,
399 offset, size, 0);
400}
401
402static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
403 size_t size)
404{
405 return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num,
406 offset, size, 1);
407}
408
409static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg)
410{
411 size_t pos = 0;
412 int i, j;
413 struct iovec *src_sg;
414 unsigned int num;
415
416 if (rx) {
417 src_sg = pdu->elem.in_sg;
418 num = pdu->elem.in_num;
419 } else {
420 src_sg = pdu->elem.out_sg;
421 num = pdu->elem.out_num;
422 }
423
424 j = 0;
425 for (i = 0; i < num; i++) {
426 if (offset <= pos) {
427 sg[j].iov_base = src_sg[i].iov_base;
428 sg[j].iov_len = src_sg[i].iov_len;
429 j++;
430 } else if (offset < (src_sg[i].iov_len + pos)) {
431 sg[j].iov_base = src_sg[i].iov_base;
432 sg[j].iov_len = src_sg[i].iov_len;
433 sg[j].iov_base += (offset - pos);
434 sg[j].iov_len -= (offset - pos);
435 j++;
436 }
437 pos += src_sg[i].iov_len;
438 }
439
440 return j;
441}
442
443static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
444{
445 size_t old_offset = offset;
446 va_list ap;
447 int i;
448
449 va_start(ap, fmt);
450 for (i = 0; fmt[i]; i++) {
451 switch (fmt[i]) {
452 case 'b': {
453 uint8_t *valp = va_arg(ap, uint8_t *);
454 offset += pdu_unpack(valp, pdu, offset, sizeof(*valp));
455 break;
456 }
457 case 'w': {
458 uint16_t val, *valp;
459 valp = va_arg(ap, uint16_t *);
460 val = le16_to_cpupu(valp);
461 offset += pdu_unpack(&val, pdu, offset, sizeof(val));
462 *valp = val;
463 break;
464 }
465 case 'd': {
466 uint32_t val, *valp;
467 valp = va_arg(ap, uint32_t *);
468 val = le32_to_cpupu(valp);
469 offset += pdu_unpack(&val, pdu, offset, sizeof(val));
470 *valp = val;
471 break;
472 }
473 case 'q': {
474 uint64_t val, *valp;
475 valp = va_arg(ap, uint64_t *);
476 val = le64_to_cpup(valp);
477 offset += pdu_unpack(&val, pdu, offset, sizeof(val));
478 *valp = val;
479 break;
480 }
481 case 'v': {
482 struct iovec *iov = va_arg(ap, struct iovec *);
483 int *iovcnt = va_arg(ap, int *);
484 *iovcnt = pdu_copy_sg(pdu, offset, 0, iov);
485 break;
486 }
487 case 's': {
488 V9fsString *str = va_arg(ap, V9fsString *);
489 offset += pdu_unmarshal(pdu, offset, "w", &str->size);
490 /* FIXME: sanity check str->size */
491 str->data = qemu_malloc(str->size + 1);
492 offset += pdu_unpack(str->data, pdu, offset, str->size);
493 str->data[str->size] = 0;
494 break;
495 }
496 case 'Q': {
497 V9fsQID *qidp = va_arg(ap, V9fsQID *);
498 offset += pdu_unmarshal(pdu, offset, "bdq",
499 &qidp->type, &qidp->version, &qidp->path);
500 break;
501 }
502 case 'S': {
503 V9fsStat *statp = va_arg(ap, V9fsStat *);
504 offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd",
505 &statp->size, &statp->type, &statp->dev,
506 &statp->qid, &statp->mode, &statp->atime,
507 &statp->mtime, &statp->length,
508 &statp->name, &statp->uid, &statp->gid,
509 &statp->muid, &statp->extension,
510 &statp->n_uid, &statp->n_gid,
511 &statp->n_muid);
512 break;
513 }
514 default:
515 break;
516 }
517 }
518
519 va_end(ap);
520
521 return offset - old_offset;
522}
523
524static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
525{
526 size_t old_offset = offset;
527 va_list ap;
528 int i;
529
530 va_start(ap, fmt);
531 for (i = 0; fmt[i]; i++) {
532 switch (fmt[i]) {
533 case 'b': {
534 uint8_t val = va_arg(ap, int);
535 offset += pdu_pack(pdu, offset, &val, sizeof(val));
536 break;
537 }
538 case 'w': {
539 uint16_t val;
540 cpu_to_le16w(&val, va_arg(ap, int));
541 offset += pdu_pack(pdu, offset, &val, sizeof(val));
542 break;
543 }
544 case 'd': {
545 uint32_t val;
546 cpu_to_le32w(&val, va_arg(ap, uint32_t));
547 offset += pdu_pack(pdu, offset, &val, sizeof(val));
548 break;
549 }
550 case 'q': {
551 uint64_t val;
552 cpu_to_le64w(&val, va_arg(ap, uint64_t));
553 offset += pdu_pack(pdu, offset, &val, sizeof(val));
554 break;
555 }
556 case 'v': {
557 struct iovec *iov = va_arg(ap, struct iovec *);
558 int *iovcnt = va_arg(ap, int *);
559 *iovcnt = pdu_copy_sg(pdu, offset, 1, iov);
560 break;
561 }
562 case 's': {
563 V9fsString *str = va_arg(ap, V9fsString *);
564 offset += pdu_marshal(pdu, offset, "w", str->size);
565 offset += pdu_pack(pdu, offset, str->data, str->size);
566 break;
567 }
568 case 'Q': {
569 V9fsQID *qidp = va_arg(ap, V9fsQID *);
570 offset += pdu_marshal(pdu, offset, "bdq",
571 qidp->type, qidp->version, qidp->path);
572 break;
573 }
574 case 'S': {
575 V9fsStat *statp = va_arg(ap, V9fsStat *);
576 offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd",
577 statp->size, statp->type, statp->dev,
578 &statp->qid, statp->mode, statp->atime,
579 statp->mtime, statp->length, &statp->name,
580 &statp->uid, &statp->gid, &statp->muid,
581 &statp->extension, statp->n_uid,
582 statp->n_gid, statp->n_muid);
583 break;
584 }
585 default:
586 break;
587 }
588 }
589 va_end(ap);
590
591 return offset - old_offset;
592}
593
594static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
595{
596 int8_t id = pdu->id + 1; /* Response */
597
598 if (len < 0) {
599 V9fsString str;
600 int err = -len;
601
602 str.data = strerror(err);
603 str.size = strlen(str.data);
604
605 len = 7;
606 len += pdu_marshal(pdu, len, "s", &str);
607 if (dotu) {
608 len += pdu_marshal(pdu, len, "d", err);
609 }
610
611 id = P9_RERROR;
612 }
613
614 /* fill out the header */
615 pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
616
617 /* keep these in sync */
618 pdu->size = len;
619 pdu->id = id;
620
621 /* push onto queue and notify */
622 virtqueue_push(s->vq, &pdu->elem, len);
623
624 /* FIXME: we should batch these completions */
625 virtio_notify(&s->vdev, s->vq);
626
627 free_pdu(s, pdu);
628}
629
bb9e3216
AL
630static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
631{
632 mode_t ret;
633
634 ret = mode & 0777;
635 if (mode & P9_STAT_MODE_DIR) {
636 ret |= S_IFDIR;
637 }
638
639 if (dotu) {
640 if (mode & P9_STAT_MODE_SYMLINK) {
641 ret |= S_IFLNK;
642 }
643 if (mode & P9_STAT_MODE_SOCKET) {
644 ret |= S_IFSOCK;
645 }
646 if (mode & P9_STAT_MODE_NAMED_PIPE) {
647 ret |= S_IFIFO;
648 }
649 if (mode & P9_STAT_MODE_DEVICE) {
650 if (extension && extension->data[0] == 'c') {
651 ret |= S_IFCHR;
652 } else {
653 ret |= S_IFBLK;
654 }
655 }
656 }
657
658 if (!(ret&~0777)) {
659 ret |= S_IFREG;
660 }
661
662 if (mode & P9_STAT_MODE_SETUID) {
663 ret |= S_ISUID;
664 }
665 if (mode & P9_STAT_MODE_SETGID) {
666 ret |= S_ISGID;
667 }
668 if (mode & P9_STAT_MODE_SETVTX) {
669 ret |= S_ISVTX;
670 }
671
672 return ret;
673}
674
675static int donttouch_stat(V9fsStat *stat)
676{
677 if (stat->type == -1 &&
678 stat->dev == -1 &&
679 stat->qid.type == -1 &&
680 stat->qid.version == -1 &&
681 stat->qid.path == -1 &&
682 stat->mode == -1 &&
683 stat->atime == -1 &&
684 stat->mtime == -1 &&
685 stat->length == -1 &&
686 !stat->name.size &&
687 !stat->uid.size &&
688 !stat->gid.size &&
689 !stat->muid.size &&
690 stat->n_uid == -1 &&
691 stat->n_gid == -1 &&
692 stat->n_muid == -1) {
693 return 1;
694 }
695
696 return 0;
697}
698
699static void v9fs_stat_free(V9fsStat *stat)
700{
701 v9fs_string_free(&stat->name);
702 v9fs_string_free(&stat->uid);
703 v9fs_string_free(&stat->gid);
704 v9fs_string_free(&stat->muid);
705 v9fs_string_free(&stat->extension);
706}
707
708static uint32_t stat_to_v9mode(const struct stat *stbuf)
709{
710 uint32_t mode;
711
712 mode = stbuf->st_mode & 0777;
713 if (S_ISDIR(stbuf->st_mode)) {
714 mode |= P9_STAT_MODE_DIR;
715 }
716
717 if (dotu) {
718 if (S_ISLNK(stbuf->st_mode)) {
719 mode |= P9_STAT_MODE_SYMLINK;
720 }
721
722 if (S_ISSOCK(stbuf->st_mode)) {
723 mode |= P9_STAT_MODE_SOCKET;
724 }
725
726 if (S_ISFIFO(stbuf->st_mode)) {
727 mode |= P9_STAT_MODE_NAMED_PIPE;
728 }
729
730 if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
731 mode |= P9_STAT_MODE_DEVICE;
732 }
733
734 if (stbuf->st_mode & S_ISUID) {
735 mode |= P9_STAT_MODE_SETUID;
736 }
737
738 if (stbuf->st_mode & S_ISGID) {
739 mode |= P9_STAT_MODE_SETGID;
740 }
741
742 if (stbuf->st_mode & S_ISVTX) {
743 mode |= P9_STAT_MODE_SETVTX;
744 }
745 }
746
747 return mode;
748}
749
750static int stat_to_v9stat(V9fsState *s, V9fsString *name,
751 const struct stat *stbuf,
752 V9fsStat *v9stat)
753{
754 int err;
755 const char *str;
756
757 memset(v9stat, 0, sizeof(*v9stat));
758
759 stat_to_qid(stbuf, &v9stat->qid);
760 v9stat->mode = stat_to_v9mode(stbuf);
761 v9stat->atime = stbuf->st_atime;
762 v9stat->mtime = stbuf->st_mtime;
763 v9stat->length = stbuf->st_size;
764
765 v9fs_string_null(&v9stat->uid);
766 v9fs_string_null(&v9stat->gid);
767 v9fs_string_null(&v9stat->muid);
768
769 if (dotu) {
770 v9stat->n_uid = stbuf->st_uid;
771 v9stat->n_gid = stbuf->st_gid;
772 v9stat->n_muid = 0;
773
774 v9fs_string_null(&v9stat->extension);
775
776 if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
777 err = v9fs_do_readlink(s, name, &v9stat->extension);
778 if (err == -1) {
779 err = -errno;
780 return err;
781 }
782 v9stat->extension.data[err] = 0;
783 v9stat->extension.size = err;
784 } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
785 v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
786 S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
787 major(stbuf->st_rdev), minor(stbuf->st_rdev));
788 } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
789 v9fs_string_sprintf(&v9stat->extension, "%s %u",
790 "HARDLINKCOUNT", stbuf->st_nlink);
791 }
792 }
793
794 str = strrchr(name->data, '/');
795 if (str) {
796 str += 1;
797 } else {
798 str = name->data;
799 }
800
801 v9fs_string_sprintf(&v9stat->name, "%s", str);
802
803 v9stat->size = 61 +
804 v9fs_string_size(&v9stat->name) +
805 v9fs_string_size(&v9stat->uid) +
806 v9fs_string_size(&v9stat->gid) +
807 v9fs_string_size(&v9stat->muid) +
808 v9fs_string_size(&v9stat->extension);
809 return 0;
810}
811
1f5a89bf
AL
812static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt)
813{
814 while (len && *iovcnt) {
815 if (len < sg->iov_len) {
816 sg->iov_len -= len;
817 sg->iov_base += len;
818 len = 0;
819 } else {
820 len -= sg->iov_len;
821 sg++;
822 *iovcnt -= 1;
823 }
824 }
825
826 return sg;
827}
828
829static struct iovec *cap_sg(struct iovec *sg, int cap, int *cnt)
830{
831 int i;
832 int total = 0;
833
834 for (i = 0; i < *cnt; i++) {
835 if ((total + sg[i].iov_len) > cap) {
836 sg[i].iov_len -= ((total + sg[i].iov_len) - cap);
837 i++;
838 break;
839 }
840 total += sg[i].iov_len;
841 }
842
843 *cnt = i;
844
845 return sg;
846}
847
848static void print_sg(struct iovec *sg, int cnt)
849{
850 int i;
851
852 printf("sg[%d]: {", cnt);
853 for (i = 0; i < cnt; i++) {
854 if (i) {
855 printf(", ");
856 }
857 printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len);
858 }
859 printf("}\n");
860}
861
405a549a
AL
862static void v9fs_dummy(V9fsState *s, V9fsPDU *pdu)
863{
864 /* Note: The following have been added to prevent GCC from complaining
865 * They will be removed in the subsequent patches */
866 (void)pdu_unmarshal;
867 (void) complete_pdu;
a03f7874
AL
868 (void) v9fs_string_init;
869 (void) v9fs_string_free;
870 (void) v9fs_string_null;
871 (void) v9fs_string_sprintf;
872 (void) v9fs_string_copy;
873 (void) v9fs_string_size;
131dcb25
AL
874 (void) v9fs_do_lstat;
875 (void) v9fs_do_setuid;
876 (void) v9fs_do_readlink;
877 (void) v9fs_do_close;
878 (void) v9fs_do_closedir;
286d5652
AL
879 (void) alloc_fid;
880 (void) free_fid;
881 (void) fid_to_qid;
bb9e3216
AL
882 (void) v9mode_to_mode;
883 (void) donttouch_stat;
884 (void) v9fs_stat_free;
885 (void) stat_to_v9stat;
1f5a89bf
AL
886 (void) adjust_sg;
887 (void) cap_sg;
888 (void) print_sg;
405a549a 889}
131dcb25 890
9f107513
AL
891static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
892{
92c1ad03
AL
893 int32_t msize;
894 V9fsString version;
895 size_t offset = 7;
896
897 pdu_unmarshal(pdu, offset, "ds", &msize, &version);
898
899 if (strcmp(version.data, "9P2000.u")) {
900 v9fs_string_sprintf(&version, "unknown");
9f107513 901 }
92c1ad03
AL
902
903 offset += pdu_marshal(pdu, offset, "ds", msize, &version);
904 complete_pdu(s, pdu, offset);
905
906 v9fs_string_free(&version);
9f107513
AL
907}
908
909static void v9fs_attach(V9fsState *s, V9fsPDU *pdu)
910{
955efc47
AL
911 int32_t fid, afid, n_uname;
912 V9fsString uname, aname;
913 V9fsFidState *fidp;
914 V9fsQID qid;
915 size_t offset = 7;
916 ssize_t err;
917
918 pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname);
919
920 fidp = alloc_fid(s, fid);
921 if (fidp == NULL) {
922 err = -EINVAL;
923 goto out;
9f107513 924 }
955efc47
AL
925
926 fidp->uid = n_uname;
927
928 v9fs_string_sprintf(&fidp->path, "%s", "/");
929 err = fid_to_qid(s, fidp, &qid);
930 if (err) {
931 err = -EINVAL;
932 free_fid(s, fid);
933 goto out;
934 }
935
936 offset += pdu_marshal(pdu, offset, "Q", &qid);
937
938 err = offset;
939out:
940 complete_pdu(s, pdu, err);
941 v9fs_string_free(&uname);
942 v9fs_string_free(&aname);
9f107513
AL
943}
944
4da7d3fa
AL
945typedef struct V9fsStatState {
946 V9fsPDU *pdu;
947 size_t offset;
948 V9fsStat v9stat;
949 V9fsFidState *fidp;
950 struct stat stbuf;
951} V9fsStatState;
952
953static void v9fs_stat_post_lstat(V9fsState *s, V9fsStatState *vs, int err)
954{
955 if (err == -1) {
956 err = -errno;
957 goto out;
958 }
959
960 err = stat_to_v9stat(s, &vs->fidp->path, &vs->stbuf, &vs->v9stat);
961 if (err) {
962 goto out;
963 }
964 vs->offset += pdu_marshal(vs->pdu, vs->offset, "wS", 0, &vs->v9stat);
965 err = vs->offset;
966
967out:
968 complete_pdu(s, vs->pdu, err);
969 v9fs_stat_free(&vs->v9stat);
970 qemu_free(vs);
971}
972
9f107513
AL
973static void v9fs_stat(V9fsState *s, V9fsPDU *pdu)
974{
4da7d3fa
AL
975 int32_t fid;
976 V9fsStatState *vs;
977 ssize_t err = 0;
978
979 vs = qemu_malloc(sizeof(*vs));
980 vs->pdu = pdu;
981 vs->offset = 7;
982
983 memset(&vs->v9stat, 0, sizeof(vs->v9stat));
984
985 pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
986
987 vs->fidp = lookup_fid(s, fid);
988 if (vs->fidp == NULL) {
989 err = -ENOENT;
990 goto out;
9f107513 991 }
4da7d3fa
AL
992
993 err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
994 v9fs_stat_post_lstat(s, vs, err);
995 return;
996
997out:
998 complete_pdu(s, vs->pdu, err);
999 v9fs_stat_free(&vs->v9stat);
1000 qemu_free(vs);
9f107513
AL
1001}
1002
ff5e54c9
AL
1003typedef struct V9fsWalkState {
1004 V9fsPDU *pdu;
1005 size_t offset;
1006 int16_t nwnames;
1007 int name_idx;
1008 V9fsQID *qids;
1009 V9fsFidState *fidp;
1010 V9fsFidState *newfidp;
1011 V9fsString path;
1012 V9fsString *wnames;
1013 struct stat stbuf;
1014} V9fsWalkState;
1015
1016static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
1017{
1018 complete_pdu(s, vs->pdu, err);
1019
1020 if (vs->nwnames) {
1021 for (vs->name_idx = 0; vs->name_idx < vs->nwnames; vs->name_idx++) {
1022 v9fs_string_free(&vs->wnames[vs->name_idx]);
1023 }
1024
1025 qemu_free(vs->wnames);
1026 qemu_free(vs->qids);
1027 }
1028}
1029
1030static void v9fs_walk_marshal(V9fsWalkState *vs)
1031{
1032 int i;
1033 vs->offset = 7;
1034 vs->offset += pdu_marshal(vs->pdu, vs->offset, "w", vs->nwnames);
1035
1036 for (i = 0; i < vs->nwnames; i++) {
1037 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qids[i]);
1038 }
1039}
1040
1041static void v9fs_walk_post_newfid_lstat(V9fsState *s, V9fsWalkState *vs,
1042 int err)
1043{
1044 if (err == -1) {
1045 free_fid(s, vs->newfidp->fid);
1046 v9fs_string_free(&vs->path);
1047 err = -ENOENT;
1048 goto out;
1049 }
1050
1051 stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
1052
1053 vs->name_idx++;
1054 if (vs->name_idx < vs->nwnames) {
1055 v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
1056 vs->wnames[vs->name_idx].data);
1057 v9fs_string_copy(&vs->newfidp->path, &vs->path);
1058
1059 err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
1060 v9fs_walk_post_newfid_lstat(s, vs, err);
1061 return;
1062 }
1063
1064 v9fs_string_free(&vs->path);
1065 v9fs_walk_marshal(vs);
1066 err = vs->offset;
1067out:
1068 v9fs_walk_complete(s, vs, err);
1069}
1070
1071static void v9fs_walk_post_oldfid_lstat(V9fsState *s, V9fsWalkState *vs,
1072 int err)
1073{
1074 if (err == -1) {
1075 v9fs_string_free(&vs->path);
1076 err = -ENOENT;
1077 goto out;
1078 }
1079
1080 stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
1081 vs->name_idx++;
1082 if (vs->name_idx < vs->nwnames) {
1083
1084 v9fs_string_sprintf(&vs->path, "%s/%s",
1085 vs->fidp->path.data, vs->wnames[vs->name_idx].data);
1086 v9fs_string_copy(&vs->fidp->path, &vs->path);
1087
1088 err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
1089 v9fs_walk_post_oldfid_lstat(s, vs, err);
1090 return;
1091 }
1092
1093 v9fs_string_free(&vs->path);
1094 v9fs_walk_marshal(vs);
1095 err = vs->offset;
1096out:
1097 v9fs_walk_complete(s, vs, err);
1098}
1099
9f107513
AL
1100static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
1101{
ff5e54c9
AL
1102 int32_t fid, newfid;
1103 V9fsWalkState *vs;
1104 int err = 0;
1105 int i;
1106
1107 vs = qemu_malloc(sizeof(*vs));
1108 vs->pdu = pdu;
1109 vs->wnames = NULL;
1110 vs->qids = NULL;
1111 vs->offset = 7;
1112
1113 vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "ddw", &fid,
1114 &newfid, &vs->nwnames);
1115
1116 if (vs->nwnames) {
1117 vs->wnames = qemu_mallocz(sizeof(vs->wnames[0]) * vs->nwnames);
1118
1119 vs->qids = qemu_mallocz(sizeof(vs->qids[0]) * vs->nwnames);
1120
1121 for (i = 0; i < vs->nwnames; i++) {
1122 vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "s",
1123 &vs->wnames[i]);
1124 }
1125 }
1126
1127 vs->fidp = lookup_fid(s, fid);
1128 if (vs->fidp == NULL) {
1129 err = -ENOENT;
1130 goto out;
1131 }
1132
1133 /* FIXME: is this really valid? */
1134 if (fid == newfid) {
1135
1136 BUG_ON(vs->fidp->fd != -1);
1137 BUG_ON(vs->fidp->dir);
1138 v9fs_string_init(&vs->path);
1139 vs->name_idx = 0;
1140
1141 if (vs->name_idx < vs->nwnames) {
1142 v9fs_string_sprintf(&vs->path, "%s/%s",
1143 vs->fidp->path.data, vs->wnames[vs->name_idx].data);
1144 v9fs_string_copy(&vs->fidp->path, &vs->path);
1145
1146 err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
1147 v9fs_walk_post_oldfid_lstat(s, vs, err);
1148 return;
1149 }
1150 } else {
1151 vs->newfidp = alloc_fid(s, newfid);
1152 if (vs->newfidp == NULL) {
1153 err = -EINVAL;
1154 goto out;
1155 }
1156
1157 vs->newfidp->uid = vs->fidp->uid;
1158 v9fs_string_init(&vs->path);
1159 vs->name_idx = 0;
1160 v9fs_string_copy(&vs->newfidp->path, &vs->fidp->path);
1161
1162 if (vs->name_idx < vs->nwnames) {
1163 v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
1164 vs->wnames[vs->name_idx].data);
1165 v9fs_string_copy(&vs->newfidp->path, &vs->path);
1166
1167 err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
1168 v9fs_walk_post_newfid_lstat(s, vs, err);
1169 return;
1170 }
9f107513 1171 }
ff5e54c9
AL
1172
1173 v9fs_walk_marshal(vs);
1174 err = vs->offset;
1175out:
1176 v9fs_walk_complete(s, vs, err);
9f107513
AL
1177}
1178
a6568fe2
AL
1179typedef struct V9fsOpenState {
1180 V9fsPDU *pdu;
1181 size_t offset;
1182 int8_t mode;
1183 V9fsFidState *fidp;
1184 V9fsQID qid;
1185 struct stat stbuf;
1186
1187} V9fsOpenState;
1188
1189enum {
1190 Oread = 0x00,
1191 Owrite = 0x01,
1192 Ordwr = 0x02,
1193 Oexec = 0x03,
1194 Oexcl = 0x04,
1195 Otrunc = 0x10,
1196 Orexec = 0x20,
1197 Orclose = 0x40,
1198 Oappend = 0x80,
1199};
1200
1201static int omode_to_uflags(int8_t mode)
9f107513 1202{
a6568fe2
AL
1203 int ret = 0;
1204
1205 switch (mode & 3) {
1206 case Oread:
1207 ret = O_RDONLY;
1208 break;
1209 case Ordwr:
1210 ret = O_RDWR;
1211 break;
1212 case Owrite:
1213 ret = O_WRONLY;
1214 break;
1215 case Oexec:
1216 ret = O_RDONLY;
1217 break;
1218 }
1219
1220 if (mode & Otrunc) {
1221 ret |= O_TRUNC;
1222 }
1223
1224 if (mode & Oappend) {
1225 ret |= O_APPEND;
9f107513 1226 }
a6568fe2
AL
1227
1228 if (mode & Oexcl) {
1229 ret |= O_EXCL;
1230 }
1231
1232 return ret;
1233}
1234
1235static void v9fs_open_post_opendir(V9fsState *s, V9fsOpenState *vs, int err)
1236{
1237 if (vs->fidp->dir == NULL) {
1238 err = -errno;
1239 goto out;
1240 }
1241
1242 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
1243 err = vs->offset;
1244out:
1245 complete_pdu(s, vs->pdu, err);
1246 qemu_free(vs);
1247
1248}
1249
1250static void v9fs_open_post_open(V9fsState *s, V9fsOpenState *vs, int err)
1251{
1252 if (vs->fidp->fd == -1) {
1253 err = -errno;
1254 goto out;
1255 }
1256
1257 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
1258 err = vs->offset;
1259out:
1260 complete_pdu(s, vs->pdu, err);
1261 qemu_free(vs);
1262}
1263
1264static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
1265{
1266 if (err) {
1267 err = -errno;
1268 goto out;
1269 }
1270
1271 stat_to_qid(&vs->stbuf, &vs->qid);
1272
1273 if (S_ISDIR(vs->stbuf.st_mode)) {
1274 vs->fidp->dir = v9fs_do_opendir(s, &vs->fidp->path);
1275 v9fs_open_post_opendir(s, vs, err);
1276 } else {
1277 vs->fidp->fd = v9fs_do_open(s, &vs->fidp->path,
1278 omode_to_uflags(vs->mode));
1279 v9fs_open_post_open(s, vs, err);
1280 }
1281 return;
1282out:
1283 complete_pdu(s, vs->pdu, err);
1284 qemu_free(vs);
9f107513
AL
1285}
1286
1287static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
a6568fe2
AL
1288{
1289 int32_t fid;
1290 V9fsOpenState *vs;
1291 ssize_t err = 0;
1292
1293
1294 vs = qemu_malloc(sizeof(*vs));
1295 vs->pdu = pdu;
1296 vs->offset = 7;
1297
1298 pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
1299
1300 vs->fidp = lookup_fid(s, fid);
1301 if (vs->fidp == NULL) {
1302 err = -ENOENT;
1303 goto out;
1304 }
1305
1306 BUG_ON(vs->fidp->fd != -1);
1307 BUG_ON(vs->fidp->dir);
1308
1309 err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
1310
1311 v9fs_open_post_lstat(s, vs, err);
1312 return;
1313out:
1314 complete_pdu(s, pdu, err);
1315 qemu_free(vs);
1316}
1317
1318static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
1319{
bbd5697b
AL
1320 int32_t fid;
1321 size_t offset = 7;
1322 int err;
1323
1324 pdu_unmarshal(pdu, offset, "d", &fid);
1325
1326 err = free_fid(s, fid);
1327 if (err < 0) {
1328 goto out;
a6568fe2 1329 }
bbd5697b
AL
1330
1331 offset = 7;
1332 err = offset;
1333out:
1334 complete_pdu(s, pdu, err);
9f107513
AL
1335}
1336
a9231555
AL
1337typedef struct V9fsReadState {
1338 V9fsPDU *pdu;
1339 size_t offset;
1340 int32_t count;
1341 int32_t total;
1342 int64_t off;
1343 V9fsFidState *fidp;
1344 struct iovec iov[128]; /* FIXME: bad, bad, bad */
1345 struct iovec *sg;
1346 off_t dir_pos;
1347 struct dirent *dent;
1348 struct stat stbuf;
1349 V9fsString name;
1350 V9fsStat v9stat;
1351 int32_t len;
1352 int32_t cnt;
1353 int32_t max_count;
1354} V9fsReadState;
1355
1356static void v9fs_read_post_readdir(V9fsState *, V9fsReadState *, ssize_t);
1357
1358static void v9fs_read_post_seekdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
1359{
1360 if (err) {
1361 goto out;
1362 }
1363 v9fs_stat_free(&vs->v9stat);
1364 v9fs_string_free(&vs->name);
1365 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
1366 vs->offset += vs->count;
1367 err = vs->offset;
1368out:
1369 complete_pdu(s, vs->pdu, err);
1370 qemu_free(vs);
1371 return;
1372}
1373
1374static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
1375 ssize_t err)
1376{
1377 if (err) {
1378 err = -errno;
1379 goto out;
1380 }
1381 err = stat_to_v9stat(s, &vs->name, &vs->stbuf, &vs->v9stat);
1382 if (err) {
1383 goto out;
1384 }
1385
1386 vs->len = pdu_marshal(vs->pdu, vs->offset + 4 + vs->count, "S",
1387 &vs->v9stat);
1388 if ((vs->len != (vs->v9stat.size + 2)) ||
1389 ((vs->count + vs->len) > vs->max_count)) {
1390 v9fs_do_seekdir(s, vs->fidp->dir, vs->dir_pos);
1391 v9fs_read_post_seekdir(s, vs, err);
1392 return;
1393 }
1394 vs->count += vs->len;
1395 v9fs_stat_free(&vs->v9stat);
1396 v9fs_string_free(&vs->name);
1397 vs->dir_pos = vs->dent->d_off;
1398 vs->dent = v9fs_do_readdir(s, vs->fidp->dir);
1399 v9fs_read_post_readdir(s, vs, err);
1400 return;
1401out:
1402 v9fs_do_seekdir(s, vs->fidp->dir, vs->dir_pos);
1403 v9fs_read_post_seekdir(s, vs, err);
1404 return;
1405
1406}
1407
1408static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
1409{
1410 if (vs->dent) {
1411 memset(&vs->v9stat, 0, sizeof(vs->v9stat));
1412 v9fs_string_init(&vs->name);
1413 v9fs_string_sprintf(&vs->name, "%s/%s", vs->fidp->path.data,
1414 vs->dent->d_name);
1415 err = v9fs_do_lstat(s, &vs->name, &vs->stbuf);
1416 v9fs_read_post_dir_lstat(s, vs, err);
1417 return;
1418 }
1419
1420 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
1421 vs->offset += vs->count;
1422 err = vs->offset;
1423 complete_pdu(s, vs->pdu, err);
1424 qemu_free(vs);
1425 return;
1426}
1427
1428static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
1429{
1430 vs->dent = v9fs_do_readdir(s, vs->fidp->dir);
1431 v9fs_read_post_readdir(s, vs, err);
1432 return;
1433}
1434
1435static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs,
1436 ssize_t err)
1437{
1438 vs->dir_pos = v9fs_do_telldir(s, vs->fidp->dir);
1439 v9fs_read_post_telldir(s, vs, err);
1440 return;
1441}
1442
1443static void v9fs_read_post_readv(V9fsState *s, V9fsReadState *vs, ssize_t err)
1444{
1445 if (err < 0) {
1446 /* IO error return the error */
1447 err = -errno;
1448 goto out;
1449 }
1450 vs->total += vs->len;
1451 vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
1452 if (vs->total < vs->count && vs->len > 0) {
1453 do {
1454 if (0) {
1455 print_sg(vs->sg, vs->cnt);
1456 }
1457 vs->len = v9fs_do_readv(s, vs->fidp->fd, vs->sg, vs->cnt);
1458 } while (vs->len == -1 && errno == EINTR);
1459 if (vs->len == -1) {
1460 err = -errno;
1461 }
1462 v9fs_read_post_readv(s, vs, err);
1463 return;
1464 }
1465 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
1466 vs->offset += vs->count;
1467 err = vs->offset;
1468
1469out:
1470 complete_pdu(s, vs->pdu, err);
1471 qemu_free(vs);
1472}
1473
1474static void v9fs_read_post_lseek(V9fsState *s, V9fsReadState *vs, ssize_t err)
1475{
1476 if (err == -1) {
1477 err = -errno;
1478 goto out;
1479 }
1480 vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
1481
1482 if (vs->total < vs->count) {
1483 do {
1484 if (0) {
1485 print_sg(vs->sg, vs->cnt);
1486 }
1487 vs->len = v9fs_do_readv(s, vs->fidp->fd, vs->sg, vs->cnt);
1488 } while (vs->len == -1 && errno == EINTR);
1489 if (vs->len == -1) {
1490 err = -errno;
1491 }
1492 v9fs_read_post_readv(s, vs, err);
1493 return;
1494 }
1495out:
1496 complete_pdu(s, vs->pdu, err);
1497 qemu_free(vs);
1498}
1499
9f107513
AL
1500static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
1501{
a9231555
AL
1502 int32_t fid;
1503 V9fsReadState *vs;
1504 ssize_t err = 0;
1505
1506 vs = qemu_malloc(sizeof(*vs));
1507 vs->pdu = pdu;
1508 vs->offset = 7;
1509 vs->total = 0;
1510 vs->len = 0;
1511 vs->count = 0;
1512
1513 pdu_unmarshal(vs->pdu, vs->offset, "dqd", &fid, &vs->off, &vs->count);
1514
1515 vs->fidp = lookup_fid(s, fid);
1516 if (vs->fidp == NULL) {
1517 err = -EINVAL;
1518 goto out;
1519 }
1520
1521 if (vs->fidp->dir) {
1522 vs->max_count = vs->count;
1523 vs->count = 0;
1524 if (vs->off == 0) {
1525 v9fs_do_rewinddir(s, vs->fidp->dir);
1526 }
1527 v9fs_read_post_rewinddir(s, vs, err);
1528 return;
1529 } else if (vs->fidp->fd != -1) {
1530 vs->sg = vs->iov;
1531 pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
1532 err = v9fs_do_lseek(s, vs->fidp->fd, vs->off, SEEK_SET);
1533 v9fs_read_post_lseek(s, vs, err);
1534 return;
1535 } else {
1536 err = -EINVAL;
9f107513 1537 }
a9231555
AL
1538out:
1539 complete_pdu(s, pdu, err);
1540 qemu_free(vs);
9f107513
AL
1541}
1542
8449360c
AL
1543typedef struct V9fsWriteState {
1544 V9fsPDU *pdu;
1545 size_t offset;
1546 int32_t len;
1547 int32_t count;
1548 int32_t total;
1549 int64_t off;
1550 V9fsFidState *fidp;
1551 struct iovec iov[128]; /* FIXME: bad, bad, bad */
1552 struct iovec *sg;
1553 int cnt;
1554} V9fsWriteState;
1555
1556static void v9fs_write_post_writev(V9fsState *s, V9fsWriteState *vs,
1557 ssize_t err)
1558{
1559 if (err < 0) {
1560 /* IO error return the error */
1561 err = -errno;
1562 goto out;
1563 }
1564 vs->total += vs->len;
1565 vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
1566 if (vs->total < vs->count && vs->len > 0) {
1567 do {
1568 if (0) {
1569 print_sg(vs->sg, vs->cnt);
1570 }
1571 vs->len = v9fs_do_writev(s, vs->fidp->fd, vs->sg, vs->cnt);
1572 } while (vs->len == -1 && errno == EINTR);
1573 if (vs->len == -1) {
1574 err = -errno;
1575 }
1576 v9fs_write_post_writev(s, vs, err);
1577 return;
1578 }
1579 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
1580
1581 err = vs->offset;
1582out:
1583 complete_pdu(s, vs->pdu, err);
1584 qemu_free(vs);
1585}
1586
1587static void v9fs_write_post_lseek(V9fsState *s, V9fsWriteState *vs, ssize_t err)
1588{
1589 if (err == -1) {
1590 err = -errno;
1591 goto out;
1592 }
1593 vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
1594
1595 if (vs->total < vs->count) {
1596 do {
1597 if (0) {
1598 print_sg(vs->sg, vs->cnt);
1599 }
1600 vs->len = v9fs_do_writev(s, vs->fidp->fd, vs->sg, vs->cnt);
1601 } while (vs->len == -1 && errno == EINTR);
1602 if (vs->len == -1) {
1603 err = -errno;
1604 }
1605 v9fs_write_post_writev(s, vs, err);
1606 return;
1607 }
1608
1609out:
1610 complete_pdu(s, vs->pdu, err);
1611 qemu_free(vs);
1612}
1613
9f107513
AL
1614static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
1615{
8449360c
AL
1616 int32_t fid;
1617 V9fsWriteState *vs;
1618 ssize_t err;
1619
1620 vs = qemu_malloc(sizeof(*vs));
1621
1622 vs->pdu = pdu;
1623 vs->offset = 7;
1624 vs->sg = vs->iov;
1625 vs->total = 0;
1626 vs->len = 0;
1627
1628 pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count,
1629 vs->sg, &vs->cnt);
1630
1631 vs->fidp = lookup_fid(s, fid);
1632 if (vs->fidp == NULL) {
1633 err = -EINVAL;
1634 goto out;
9f107513 1635 }
8449360c
AL
1636
1637 if (vs->fidp->fd == -1) {
1638 err = -EINVAL;
1639 goto out;
1640 }
1641
1642 err = v9fs_do_lseek(s, vs->fidp->fd, vs->off, SEEK_SET);
1643
1644 v9fs_write_post_lseek(s, vs, err);
1645 return;
1646
1647out:
1648 complete_pdu(s, vs->pdu, err);
1649 qemu_free(vs);
9f107513
AL
1650}
1651
1652static void v9fs_create(V9fsState *s, V9fsPDU *pdu)
1653{
1654 if (debug_9p_pdu) {
1655 pprint_pdu(pdu);
1656 }
1657}
1658
1659static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
1660{
405a549a 1661 v9fs_dummy(s, pdu);
9f107513
AL
1662 if (debug_9p_pdu) {
1663 pprint_pdu(pdu);
1664 }
1665}
1666
1667static void v9fs_remove(V9fsState *s, V9fsPDU *pdu)
1668{
1669 if (debug_9p_pdu) {
1670 pprint_pdu(pdu);
1671 }
1672}
1673
1674static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
1675{
1676 if (debug_9p_pdu) {
1677 pprint_pdu(pdu);
1678 }
1679}
1680
1681typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
1682
1683static pdu_handler_t *pdu_handlers[] = {
1684 [P9_TVERSION] = v9fs_version,
1685 [P9_TATTACH] = v9fs_attach,
1686 [P9_TSTAT] = v9fs_stat,
1687 [P9_TWALK] = v9fs_walk,
1688 [P9_TCLUNK] = v9fs_clunk,
1689 [P9_TOPEN] = v9fs_open,
1690 [P9_TREAD] = v9fs_read,
1691#if 0
1692 [P9_TAUTH] = v9fs_auth,
1693#endif
1694 [P9_TFLUSH] = v9fs_flush,
1695 [P9_TCREATE] = v9fs_create,
1696 [P9_TWRITE] = v9fs_write,
1697 [P9_TWSTAT] = v9fs_wstat,
1698 [P9_TREMOVE] = v9fs_remove,
1699};
1700
1701static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
1702{
1703 pdu_handler_t *handler;
1704
1705 if (debug_9p_pdu) {
1706 pprint_pdu(pdu);
1707 }
1708
1709 BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers));
1710
1711 handler = pdu_handlers[pdu->id];
1712 BUG_ON(handler == NULL);
1713
1714 handler(s, pdu);
1715}
1716
1717static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
1718{
1719 V9fsState *s = (V9fsState *)vdev;
1720 V9fsPDU *pdu;
1721 ssize_t len;
1722
1723 while ((pdu = alloc_pdu(s)) &&
1724 (len = virtqueue_pop(vq, &pdu->elem)) != 0) {
1725 uint8_t *ptr;
1726
1727 BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
1728 BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
1729
1730 ptr = pdu->elem.out_sg[0].iov_base;
1731
1732 memcpy(&pdu->size, ptr, 4);
1733 pdu->id = ptr[4];
1734 memcpy(&pdu->tag, ptr + 5, 2);
1735
1736 submit_pdu(s, pdu);
1737 }
1738
1739 free_pdu(s, pdu);
1740}
1741
1742static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
1743{
1744 features |= 1 << VIRTIO_9P_MOUNT_TAG;
1745 return features;
1746}
1747
1748static V9fsState *to_virtio_9p(VirtIODevice *vdev)
1749{
1750 return (V9fsState *)vdev;
1751}
1752
1753static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
1754{
1755 struct virtio_9p_config *cfg;
1756 V9fsState *s = to_virtio_9p(vdev);
1757
1758 cfg = qemu_mallocz(sizeof(struct virtio_9p_config) +
1759 s->tag_len);
1760 stw_raw(&cfg->tag_len, s->tag_len);
1761 memcpy(cfg->tag, s->tag, s->tag_len);
1762 memcpy(config, cfg, s->config_size);
1763 qemu_free(cfg);
1764}
1765
1766VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
1767 {
1768 V9fsState *s;
1769 int i, len;
1770 struct stat stat;
1771 FsTypeEntry *fse;
1772
1773
1774 s = (V9fsState *)virtio_common_init("virtio-9p",
1775 VIRTIO_ID_9P,
1776 sizeof(struct virtio_9p_config)+
1777 MAX_TAG_LEN,
1778 sizeof(V9fsState));
1779
1780 /* initialize pdu allocator */
1781 QLIST_INIT(&s->free_list);
1782 for (i = 0; i < (MAX_REQ - 1); i++) {
1783 QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
1784 }
1785
1786 s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output);
1787
1788 fse = get_fsdev_fsentry(conf->fsdev_id);
1789
1790 if (!fse) {
1791 /* We don't have a fsdev identified by fsdev_id */
1792 fprintf(stderr, "Virtio-9p device couldn't find fsdev "
1793 "with the id %s\n", conf->fsdev_id);
1794 exit(1);
1795 }
1796
1797 if (!fse->path || !conf->tag) {
1798 /* we haven't specified a mount_tag or the path */
1799 fprintf(stderr, "fsdev with id %s needs path "
1800 "and Virtio-9p device needs mount_tag arguments\n",
1801 conf->fsdev_id);
1802 exit(1);
1803 }
1804
1805 if (lstat(fse->path, &stat)) {
1806 fprintf(stderr, "share path %s does not exist\n", fse->path);
1807 exit(1);
1808 } else if (!S_ISDIR(stat.st_mode)) {
1809 fprintf(stderr, "share path %s is not a directory \n", fse->path);
1810 exit(1);
1811 }
1812
1813 s->ctx.fs_root = qemu_strdup(fse->path);
1814 len = strlen(conf->tag);
1815 if (len > MAX_TAG_LEN) {
1816 len = MAX_TAG_LEN;
1817 }
1818 /* s->tag is non-NULL terminated string */
1819 s->tag = qemu_malloc(len);
1820 memcpy(s->tag, conf->tag, len);
1821 s->tag_len = len;
1822 s->ctx.uid = -1;
1823
1824 s->ops = fse->ops;
1825 s->vdev.get_features = virtio_9p_get_features;
1826 s->config_size = sizeof(struct virtio_9p_config) +
1827 s->tag_len;
1828 s->vdev.get_config = virtio_9p_get_config;
1829
1830 return &s->vdev;
1831}