]> git.proxmox.com Git - mirror_qemu.git/blame - hw/virtio-9p.c
virtio-9p: Add fidtype so that we can do type specific operation
[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
fac4f111
VJ
24enum {
25 Oread = 0x00,
26 Owrite = 0x01,
27 Ordwr = 0x02,
28 Oexec = 0x03,
29 Oexcl = 0x04,
30 Otrunc = 0x10,
31 Orexec = 0x20,
32 Orclose = 0x40,
33 Oappend = 0x80,
34};
35
36static int omode_to_uflags(int8_t mode)
37{
38 int ret = 0;
39
40 switch (mode & 3) {
41 case Oread:
42 ret = O_RDONLY;
43 break;
44 case Ordwr:
45 ret = O_RDWR;
46 break;
47 case Owrite:
48 ret = O_WRONLY;
49 break;
50 case Oexec:
51 ret = O_RDONLY;
52 break;
53 }
54
55 if (mode & Otrunc) {
56 ret |= O_TRUNC;
57 }
58
59 if (mode & Oappend) {
60 ret |= O_APPEND;
61 }
62
63 if (mode & Oexcl) {
64 ret |= O_EXCL;
65 }
66
67 return ret;
68}
69
758e8e38 70void cred_init(FsCred *credp)
131dcb25 71{
758e8e38
VJ
72 credp->fc_uid = -1;
73 credp->fc_gid = -1;
74 credp->fc_mode = -1;
75 credp->fc_rdev = -1;
131dcb25
AL
76}
77
758e8e38 78static int v9fs_do_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf)
131dcb25 79{
758e8e38 80 return s->ops->lstat(&s->ctx, path->data, stbuf);
131dcb25
AL
81}
82
83static ssize_t v9fs_do_readlink(V9fsState *s, V9fsString *path, V9fsString *buf)
84{
85 ssize_t len;
86
87 buf->data = qemu_malloc(1024);
88
89 len = s->ops->readlink(&s->ctx, path->data, buf->data, 1024 - 1);
90 if (len > -1) {
91 buf->size = len;
92 buf->data[len] = 0;
93 }
94
95 return len;
96}
97
98static int v9fs_do_close(V9fsState *s, int fd)
99{
100 return s->ops->close(&s->ctx, fd);
101}
102
103static int v9fs_do_closedir(V9fsState *s, DIR *dir)
104{
105 return s->ops->closedir(&s->ctx, dir);
106}
107
a6568fe2
AL
108static int v9fs_do_open(V9fsState *s, V9fsString *path, int flags)
109{
110 return s->ops->open(&s->ctx, path->data, flags);
111}
112
113static DIR *v9fs_do_opendir(V9fsState *s, V9fsString *path)
114{
115 return s->ops->opendir(&s->ctx, path->data);
116}
117
a9231555
AL
118static void v9fs_do_rewinddir(V9fsState *s, DIR *dir)
119{
120 return s->ops->rewinddir(&s->ctx, dir);
121}
122
123static off_t v9fs_do_telldir(V9fsState *s, DIR *dir)
124{
125 return s->ops->telldir(&s->ctx, dir);
126}
127
128static struct dirent *v9fs_do_readdir(V9fsState *s, DIR *dir)
129{
130 return s->ops->readdir(&s->ctx, dir);
131}
132
133static void v9fs_do_seekdir(V9fsState *s, DIR *dir, off_t off)
134{
135 return s->ops->seekdir(&s->ctx, dir, off);
136}
137
138static int v9fs_do_readv(V9fsState *s, int fd, const struct iovec *iov,
139 int iovcnt)
140{
141 return s->ops->readv(&s->ctx, fd, iov, iovcnt);
142}
143
144static off_t v9fs_do_lseek(V9fsState *s, int fd, off_t offset, int whence)
145{
146 return s->ops->lseek(&s->ctx, fd, offset, whence);
147}
148
8449360c
AL
149static int v9fs_do_writev(V9fsState *s, int fd, const struct iovec *iov,
150 int iovcnt)
151{
152 return s->ops->writev(&s->ctx, fd, iov, iovcnt);
153}
154
c494dd6f
AL
155static int v9fs_do_chmod(V9fsState *s, V9fsString *path, mode_t mode)
156{
e95ead32
VJ
157 FsCred cred;
158 cred_init(&cred);
159 cred.fc_mode = mode;
160 return s->ops->chmod(&s->ctx, path->data, &cred);
c494dd6f
AL
161}
162
5268cecc
MK
163static int v9fs_do_mknod(V9fsState *s, char *name,
164 mode_t mode, dev_t dev, uid_t uid, gid_t gid)
c494dd6f 165{
1c293312
VJ
166 FsCred cred;
167 cred_init(&cred);
5268cecc
MK
168 cred.fc_uid = uid;
169 cred.fc_gid = gid;
1c293312
VJ
170 cred.fc_mode = mode;
171 cred.fc_rdev = dev;
5268cecc 172 return s->ops->mknod(&s->ctx, name, &cred);
c494dd6f
AL
173}
174
b67592ea
MK
175static int v9fs_do_mkdir(V9fsState *s, char *name, mode_t mode,
176 uid_t uid, gid_t gid)
c494dd6f 177{
00ec5c37
VJ
178 FsCred cred;
179
180 cred_init(&cred);
b67592ea
MK
181 cred.fc_uid = uid;
182 cred.fc_gid = gid;
183 cred.fc_mode = mode;
00ec5c37 184
b67592ea 185 return s->ops->mkdir(&s->ctx, name, &cred);
c494dd6f
AL
186}
187
188static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf)
189{
190 return s->ops->fstat(&s->ctx, fd, stbuf);
191}
192
c1568af5
VJ
193static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid,
194 int flags, int mode)
c494dd6f 195{
4750a96f 196 FsCred cred;
4750a96f
VJ
197
198 cred_init(&cred);
c1568af5
VJ
199 cred.fc_uid = uid;
200 cred.fc_gid = gid;
201 cred.fc_mode = mode & 07777;
202 flags = flags;
4750a96f 203
c1568af5 204 return s->ops->open2(&s->ctx, fullname, flags, &cred);
c494dd6f
AL
205}
206
08c60fc9
VJ
207static int v9fs_do_symlink(V9fsState *s, V9fsFidState *fidp,
208 const char *oldpath, const char *newpath, gid_t gid)
c494dd6f 209{
879c2813
VJ
210 FsCred cred;
211 cred_init(&cred);
08c60fc9
VJ
212 cred.fc_uid = fidp->uid;
213 cred.fc_gid = gid;
214 cred.fc_mode = 0777;
879c2813 215
08c60fc9 216 return s->ops->symlink(&s->ctx, oldpath, newpath, &cred);
c494dd6f
AL
217}
218
219static int v9fs_do_link(V9fsState *s, V9fsString *oldpath, V9fsString *newpath)
220{
221 return s->ops->link(&s->ctx, oldpath->data, newpath->data);
222}
223
8cf89e00
AL
224static int v9fs_do_truncate(V9fsState *s, V9fsString *path, off_t size)
225{
226 return s->ops->truncate(&s->ctx, path->data, size);
227}
228
229static int v9fs_do_rename(V9fsState *s, V9fsString *oldpath,
230 V9fsString *newpath)
231{
232 return s->ops->rename(&s->ctx, oldpath->data, newpath->data);
233}
234
235static int v9fs_do_chown(V9fsState *s, V9fsString *path, uid_t uid, gid_t gid)
236{
f7613bee
VJ
237 FsCred cred;
238 cred_init(&cred);
239 cred.fc_uid = uid;
240 cred.fc_gid = gid;
241
242 return s->ops->chown(&s->ctx, path->data, &cred);
8cf89e00
AL
243}
244
8fc39ae4
SK
245static int v9fs_do_utimensat(V9fsState *s, V9fsString *path,
246 const struct timespec times[2])
8cf89e00 247{
8fc39ae4 248 return s->ops->utimensat(&s->ctx, path->data, times);
8cf89e00
AL
249}
250
5bae1900
AL
251static int v9fs_do_remove(V9fsState *s, V9fsString *path)
252{
253 return s->ops->remove(&s->ctx, path->data);
254}
255
8cf89e00
AL
256static int v9fs_do_fsync(V9fsState *s, int fd)
257{
258 return s->ops->fsync(&s->ctx, fd);
259}
260
5e94c103
MK
261static int v9fs_do_statfs(V9fsState *s, V9fsString *path, struct statfs *stbuf)
262{
263 return s->ops->statfs(&s->ctx, path->data, stbuf);
264}
265
a03f7874
AL
266static void v9fs_string_init(V9fsString *str)
267{
268 str->data = NULL;
269 str->size = 0;
270}
271
272static void v9fs_string_free(V9fsString *str)
273{
274 qemu_free(str->data);
275 str->data = NULL;
276 str->size = 0;
277}
278
279static void v9fs_string_null(V9fsString *str)
280{
281 v9fs_string_free(str);
282}
283
284static int number_to_string(void *arg, char type)
285{
286 unsigned int ret = 0;
287
288 switch (type) {
289 case 'u': {
290 unsigned int num = *(unsigned int *)arg;
291
292 do {
293 ret++;
294 num = num/10;
295 } while (num);
296 break;
297 }
298 default:
299 printf("Number_to_string: Unknown number format\n");
300 return -1;
301 }
302
303 return ret;
304}
305
306static int v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
307{
308 va_list ap2;
309 char *iter = (char *)fmt;
310 int len = 0;
311 int nr_args = 0;
312 char *arg_char_ptr;
313 unsigned int arg_uint;
314
315 /* Find the number of %'s that denotes an argument */
316 for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
317 nr_args++;
318 iter++;
319 }
320
321 len = strlen(fmt) - 2*nr_args;
322
323 if (!nr_args) {
324 goto alloc_print;
325 }
326
327 va_copy(ap2, ap);
328
329 iter = (char *)fmt;
330
331 /* Now parse the format string */
332 for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
333 iter++;
334 switch (*iter) {
335 case 'u':
336 arg_uint = va_arg(ap2, unsigned int);
337 len += number_to_string((void *)&arg_uint, 'u');
338 break;
339 case 's':
340 arg_char_ptr = va_arg(ap2, char *);
341 len += strlen(arg_char_ptr);
342 break;
343 case 'c':
344 len += 1;
345 break;
346 default:
347 fprintf(stderr,
348 "v9fs_string_alloc_printf:Incorrect format %c", *iter);
349 return -1;
350 }
351 iter++;
352 }
353
354alloc_print:
355 *strp = qemu_malloc((len + 1) * sizeof(**strp));
356
357 return vsprintf(*strp, fmt, ap);
358}
359
360static void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
361{
362 va_list ap;
363 int err;
364
365 v9fs_string_free(str);
366
367 va_start(ap, fmt);
368 err = v9fs_string_alloc_printf(&str->data, fmt, ap);
369 BUG_ON(err == -1);
370 va_end(ap);
371
372 str->size = err;
373}
374
375static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
376{
377 v9fs_string_free(lhs);
378 v9fs_string_sprintf(lhs, "%s", rhs->data);
379}
380
381static size_t v9fs_string_size(V9fsString *str)
382{
383 return str->size;
384}
385
286d5652
AL
386static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid)
387{
388 V9fsFidState *f;
389
390 for (f = s->fid_list; f; f = f->next) {
391 if (f->fid == fid) {
286d5652
AL
392 return f;
393 }
394 }
395
396 return NULL;
397}
398
399static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
400{
401 V9fsFidState *f;
402
403 f = lookup_fid(s, fid);
404 if (f) {
405 return NULL;
406 }
407
408 f = qemu_mallocz(sizeof(V9fsFidState));
409
410 f->fid = fid;
d62dbb51 411 f->fid_type = P9_FID_NONE;
286d5652
AL
412
413 f->next = s->fid_list;
414 s->fid_list = f;
415
416 return f;
417}
418
419static int free_fid(V9fsState *s, int32_t fid)
420{
421 V9fsFidState **fidpp, *fidp;
422
423 for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
424 if ((*fidpp)->fid == fid) {
425 break;
426 }
427 }
428
429 if (*fidpp == NULL) {
430 return -ENOENT;
431 }
432
433 fidp = *fidpp;
434 *fidpp = fidp->next;
435
d62dbb51
AK
436 if (fidp->fid_type == P9_FID_FILE) {
437 v9fs_do_close(s, fidp->fs.fd);
438 } else if (fidp->fid_type == P9_FID_DIR) {
439 v9fs_do_closedir(s, fidp->fs.dir);
440 } else if (fidp->fid_type == P9_FID_XATTR) {
441 if (fidp->fs.xattr.value) {
442 qemu_free(fidp->fs.xattr.value);
443 }
286d5652
AL
444 }
445 v9fs_string_free(&fidp->path);
446 qemu_free(fidp);
447
448 return 0;
449}
450
451#define P9_QID_TYPE_DIR 0x80
452#define P9_QID_TYPE_SYMLINK 0x02
453
454#define P9_STAT_MODE_DIR 0x80000000
455#define P9_STAT_MODE_APPEND 0x40000000
456#define P9_STAT_MODE_EXCL 0x20000000
457#define P9_STAT_MODE_MOUNT 0x10000000
458#define P9_STAT_MODE_AUTH 0x08000000
459#define P9_STAT_MODE_TMP 0x04000000
460#define P9_STAT_MODE_SYMLINK 0x02000000
461#define P9_STAT_MODE_LINK 0x01000000
462#define P9_STAT_MODE_DEVICE 0x00800000
463#define P9_STAT_MODE_NAMED_PIPE 0x00200000
464#define P9_STAT_MODE_SOCKET 0x00100000
465#define P9_STAT_MODE_SETUID 0x00080000
466#define P9_STAT_MODE_SETGID 0x00040000
467#define P9_STAT_MODE_SETVTX 0x00010000
468
469#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR | \
470 P9_STAT_MODE_SYMLINK | \
471 P9_STAT_MODE_LINK | \
472 P9_STAT_MODE_DEVICE | \
473 P9_STAT_MODE_NAMED_PIPE | \
474 P9_STAT_MODE_SOCKET)
475
476/* This is the algorithm from ufs in spfs */
477static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
478{
479 size_t size;
480
481 size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
482 memcpy(&qidp->path, &stbuf->st_ino, size);
483 qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
484 qidp->type = 0;
485 if (S_ISDIR(stbuf->st_mode)) {
486 qidp->type |= P9_QID_TYPE_DIR;
487 }
488 if (S_ISLNK(stbuf->st_mode)) {
489 qidp->type |= P9_QID_TYPE_SYMLINK;
490 }
491}
492
493static int fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp)
494{
495 struct stat stbuf;
496 int err;
497
498 err = v9fs_do_lstat(s, &fidp->path, &stbuf);
499 if (err) {
500 return err;
501 }
502
503 stat_to_qid(&stbuf, qidp);
504 return 0;
505}
506
9f107513
AL
507static V9fsPDU *alloc_pdu(V9fsState *s)
508{
509 V9fsPDU *pdu = NULL;
510
511 if (!QLIST_EMPTY(&s->free_list)) {
512 pdu = QLIST_FIRST(&s->free_list);
513 QLIST_REMOVE(pdu, next);
514 }
515 return pdu;
516}
517
518static void free_pdu(V9fsState *s, V9fsPDU *pdu)
519{
520 if (pdu) {
521 QLIST_INSERT_HEAD(&s->free_list, pdu, next);
522 }
523}
524
525size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
526 size_t offset, size_t size, int pack)
527{
528 int i = 0;
529 size_t copied = 0;
530
531 for (i = 0; size && i < sg_count; i++) {
532 size_t len;
533 if (offset >= sg[i].iov_len) {
534 /* skip this sg */
535 offset -= sg[i].iov_len;
536 continue;
537 } else {
538 len = MIN(sg[i].iov_len - offset, size);
539 if (pack) {
540 memcpy(sg[i].iov_base + offset, addr, len);
541 } else {
542 memcpy(addr, sg[i].iov_base + offset, len);
543 }
544 size -= len;
545 copied += len;
546 addr += len;
547 if (size) {
548 offset = 0;
549 continue;
550 }
551 }
552 }
553
554 return copied;
555}
556
405a549a
AL
557static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
558{
559 return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num,
560 offset, size, 0);
561}
562
563static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
564 size_t size)
565{
566 return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num,
567 offset, size, 1);
568}
569
570static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg)
571{
572 size_t pos = 0;
573 int i, j;
574 struct iovec *src_sg;
575 unsigned int num;
576
577 if (rx) {
578 src_sg = pdu->elem.in_sg;
579 num = pdu->elem.in_num;
580 } else {
581 src_sg = pdu->elem.out_sg;
582 num = pdu->elem.out_num;
583 }
584
585 j = 0;
586 for (i = 0; i < num; i++) {
587 if (offset <= pos) {
588 sg[j].iov_base = src_sg[i].iov_base;
589 sg[j].iov_len = src_sg[i].iov_len;
590 j++;
591 } else if (offset < (src_sg[i].iov_len + pos)) {
592 sg[j].iov_base = src_sg[i].iov_base;
593 sg[j].iov_len = src_sg[i].iov_len;
594 sg[j].iov_base += (offset - pos);
595 sg[j].iov_len -= (offset - pos);
596 j++;
597 }
598 pos += src_sg[i].iov_len;
599 }
600
601 return j;
602}
603
604static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
605{
606 size_t old_offset = offset;
607 va_list ap;
608 int i;
609
610 va_start(ap, fmt);
611 for (i = 0; fmt[i]; i++) {
612 switch (fmt[i]) {
613 case 'b': {
614 uint8_t *valp = va_arg(ap, uint8_t *);
615 offset += pdu_unpack(valp, pdu, offset, sizeof(*valp));
616 break;
617 }
618 case 'w': {
619 uint16_t val, *valp;
620 valp = va_arg(ap, uint16_t *);
621 val = le16_to_cpupu(valp);
622 offset += pdu_unpack(&val, pdu, offset, sizeof(val));
623 *valp = val;
624 break;
625 }
626 case 'd': {
627 uint32_t val, *valp;
628 valp = va_arg(ap, uint32_t *);
629 val = le32_to_cpupu(valp);
630 offset += pdu_unpack(&val, pdu, offset, sizeof(val));
631 *valp = val;
632 break;
633 }
634 case 'q': {
635 uint64_t val, *valp;
636 valp = va_arg(ap, uint64_t *);
637 val = le64_to_cpup(valp);
638 offset += pdu_unpack(&val, pdu, offset, sizeof(val));
639 *valp = val;
640 break;
641 }
642 case 'v': {
643 struct iovec *iov = va_arg(ap, struct iovec *);
644 int *iovcnt = va_arg(ap, int *);
645 *iovcnt = pdu_copy_sg(pdu, offset, 0, iov);
646 break;
647 }
648 case 's': {
649 V9fsString *str = va_arg(ap, V9fsString *);
650 offset += pdu_unmarshal(pdu, offset, "w", &str->size);
651 /* FIXME: sanity check str->size */
652 str->data = qemu_malloc(str->size + 1);
653 offset += pdu_unpack(str->data, pdu, offset, str->size);
654 str->data[str->size] = 0;
655 break;
656 }
657 case 'Q': {
658 V9fsQID *qidp = va_arg(ap, V9fsQID *);
659 offset += pdu_unmarshal(pdu, offset, "bdq",
660 &qidp->type, &qidp->version, &qidp->path);
661 break;
662 }
663 case 'S': {
664 V9fsStat *statp = va_arg(ap, V9fsStat *);
665 offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd",
666 &statp->size, &statp->type, &statp->dev,
667 &statp->qid, &statp->mode, &statp->atime,
668 &statp->mtime, &statp->length,
669 &statp->name, &statp->uid, &statp->gid,
670 &statp->muid, &statp->extension,
671 &statp->n_uid, &statp->n_gid,
672 &statp->n_muid);
673 break;
674 }
c79ce737
SK
675 case 'I': {
676 V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
677 offset += pdu_unmarshal(pdu, offset, "ddddqqqqq",
678 &iattr->valid, &iattr->mode,
679 &iattr->uid, &iattr->gid, &iattr->size,
680 &iattr->atime_sec, &iattr->atime_nsec,
681 &iattr->mtime_sec, &iattr->mtime_nsec);
682 break;
683 }
405a549a
AL
684 default:
685 break;
686 }
687 }
688
689 va_end(ap);
690
691 return offset - old_offset;
692}
693
694static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
695{
696 size_t old_offset = offset;
697 va_list ap;
698 int i;
699
700 va_start(ap, fmt);
701 for (i = 0; fmt[i]; i++) {
702 switch (fmt[i]) {
703 case 'b': {
704 uint8_t val = va_arg(ap, int);
705 offset += pdu_pack(pdu, offset, &val, sizeof(val));
706 break;
707 }
708 case 'w': {
709 uint16_t val;
710 cpu_to_le16w(&val, va_arg(ap, int));
711 offset += pdu_pack(pdu, offset, &val, sizeof(val));
712 break;
713 }
714 case 'd': {
715 uint32_t val;
716 cpu_to_le32w(&val, va_arg(ap, uint32_t));
717 offset += pdu_pack(pdu, offset, &val, sizeof(val));
718 break;
719 }
720 case 'q': {
721 uint64_t val;
722 cpu_to_le64w(&val, va_arg(ap, uint64_t));
723 offset += pdu_pack(pdu, offset, &val, sizeof(val));
724 break;
725 }
726 case 'v': {
727 struct iovec *iov = va_arg(ap, struct iovec *);
728 int *iovcnt = va_arg(ap, int *);
729 *iovcnt = pdu_copy_sg(pdu, offset, 1, iov);
730 break;
731 }
732 case 's': {
733 V9fsString *str = va_arg(ap, V9fsString *);
734 offset += pdu_marshal(pdu, offset, "w", str->size);
735 offset += pdu_pack(pdu, offset, str->data, str->size);
736 break;
737 }
738 case 'Q': {
739 V9fsQID *qidp = va_arg(ap, V9fsQID *);
740 offset += pdu_marshal(pdu, offset, "bdq",
741 qidp->type, qidp->version, qidp->path);
742 break;
743 }
744 case 'S': {
745 V9fsStat *statp = va_arg(ap, V9fsStat *);
746 offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd",
747 statp->size, statp->type, statp->dev,
748 &statp->qid, statp->mode, statp->atime,
749 statp->mtime, statp->length, &statp->name,
750 &statp->uid, &statp->gid, &statp->muid,
751 &statp->extension, statp->n_uid,
752 statp->n_gid, statp->n_muid);
753 break;
754 }
00ede4c2
SK
755 case 'A': {
756 V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
757 offset += pdu_marshal(pdu, offset, "qQdddqqqqqqqqqqqqqqq",
758 statp->st_result_mask,
759 &statp->qid, statp->st_mode,
760 statp->st_uid, statp->st_gid,
761 statp->st_nlink, statp->st_rdev,
762 statp->st_size, statp->st_blksize, statp->st_blocks,
763 statp->st_atime_sec, statp->st_atime_nsec,
764 statp->st_mtime_sec, statp->st_mtime_nsec,
765 statp->st_ctime_sec, statp->st_ctime_nsec,
766 statp->st_btime_sec, statp->st_btime_nsec,
767 statp->st_gen, statp->st_data_version);
768 break;
769 }
405a549a
AL
770 default:
771 break;
772 }
773 }
774 va_end(ap);
775
776 return offset - old_offset;
777}
778
779static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
780{
781 int8_t id = pdu->id + 1; /* Response */
782
783 if (len < 0) {
784 V9fsString str;
785 int err = -len;
786
787 str.data = strerror(err);
788 str.size = strlen(str.data);
789
790 len = 7;
791 len += pdu_marshal(pdu, len, "s", &str);
792 if (dotu) {
793 len += pdu_marshal(pdu, len, "d", err);
794 }
795
796 id = P9_RERROR;
797 }
798
799 /* fill out the header */
800 pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
801
802 /* keep these in sync */
803 pdu->size = len;
804 pdu->id = id;
805
806 /* push onto queue and notify */
807 virtqueue_push(s->vq, &pdu->elem, len);
808
809 /* FIXME: we should batch these completions */
810 virtio_notify(&s->vdev, s->vq);
811
812 free_pdu(s, pdu);
813}
814
bb9e3216
AL
815static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
816{
817 mode_t ret;
818
819 ret = mode & 0777;
820 if (mode & P9_STAT_MODE_DIR) {
821 ret |= S_IFDIR;
822 }
823
824 if (dotu) {
825 if (mode & P9_STAT_MODE_SYMLINK) {
826 ret |= S_IFLNK;
827 }
828 if (mode & P9_STAT_MODE_SOCKET) {
829 ret |= S_IFSOCK;
830 }
831 if (mode & P9_STAT_MODE_NAMED_PIPE) {
832 ret |= S_IFIFO;
833 }
834 if (mode & P9_STAT_MODE_DEVICE) {
835 if (extension && extension->data[0] == 'c') {
836 ret |= S_IFCHR;
837 } else {
838 ret |= S_IFBLK;
839 }
840 }
841 }
842
843 if (!(ret&~0777)) {
844 ret |= S_IFREG;
845 }
846
847 if (mode & P9_STAT_MODE_SETUID) {
848 ret |= S_ISUID;
849 }
850 if (mode & P9_STAT_MODE_SETGID) {
851 ret |= S_ISGID;
852 }
853 if (mode & P9_STAT_MODE_SETVTX) {
854 ret |= S_ISVTX;
855 }
856
857 return ret;
858}
859
860static int donttouch_stat(V9fsStat *stat)
861{
862 if (stat->type == -1 &&
863 stat->dev == -1 &&
864 stat->qid.type == -1 &&
865 stat->qid.version == -1 &&
866 stat->qid.path == -1 &&
867 stat->mode == -1 &&
868 stat->atime == -1 &&
869 stat->mtime == -1 &&
870 stat->length == -1 &&
871 !stat->name.size &&
872 !stat->uid.size &&
873 !stat->gid.size &&
874 !stat->muid.size &&
875 stat->n_uid == -1 &&
876 stat->n_gid == -1 &&
877 stat->n_muid == -1) {
878 return 1;
879 }
880
881 return 0;
882}
883
884static void v9fs_stat_free(V9fsStat *stat)
885{
886 v9fs_string_free(&stat->name);
887 v9fs_string_free(&stat->uid);
888 v9fs_string_free(&stat->gid);
889 v9fs_string_free(&stat->muid);
890 v9fs_string_free(&stat->extension);
891}
892
893static uint32_t stat_to_v9mode(const struct stat *stbuf)
894{
895 uint32_t mode;
896
897 mode = stbuf->st_mode & 0777;
898 if (S_ISDIR(stbuf->st_mode)) {
899 mode |= P9_STAT_MODE_DIR;
900 }
901
902 if (dotu) {
903 if (S_ISLNK(stbuf->st_mode)) {
904 mode |= P9_STAT_MODE_SYMLINK;
905 }
906
907 if (S_ISSOCK(stbuf->st_mode)) {
908 mode |= P9_STAT_MODE_SOCKET;
909 }
910
911 if (S_ISFIFO(stbuf->st_mode)) {
912 mode |= P9_STAT_MODE_NAMED_PIPE;
913 }
914
915 if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
916 mode |= P9_STAT_MODE_DEVICE;
917 }
918
919 if (stbuf->st_mode & S_ISUID) {
920 mode |= P9_STAT_MODE_SETUID;
921 }
922
923 if (stbuf->st_mode & S_ISGID) {
924 mode |= P9_STAT_MODE_SETGID;
925 }
926
927 if (stbuf->st_mode & S_ISVTX) {
928 mode |= P9_STAT_MODE_SETVTX;
929 }
930 }
931
932 return mode;
933}
934
935static int stat_to_v9stat(V9fsState *s, V9fsString *name,
936 const struct stat *stbuf,
937 V9fsStat *v9stat)
938{
939 int err;
940 const char *str;
941
942 memset(v9stat, 0, sizeof(*v9stat));
943
944 stat_to_qid(stbuf, &v9stat->qid);
945 v9stat->mode = stat_to_v9mode(stbuf);
946 v9stat->atime = stbuf->st_atime;
947 v9stat->mtime = stbuf->st_mtime;
948 v9stat->length = stbuf->st_size;
949
950 v9fs_string_null(&v9stat->uid);
951 v9fs_string_null(&v9stat->gid);
952 v9fs_string_null(&v9stat->muid);
953
954 if (dotu) {
955 v9stat->n_uid = stbuf->st_uid;
956 v9stat->n_gid = stbuf->st_gid;
957 v9stat->n_muid = 0;
958
959 v9fs_string_null(&v9stat->extension);
960
961 if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
962 err = v9fs_do_readlink(s, name, &v9stat->extension);
963 if (err == -1) {
964 err = -errno;
965 return err;
966 }
967 v9stat->extension.data[err] = 0;
968 v9stat->extension.size = err;
969 } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
970 v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
971 S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
972 major(stbuf->st_rdev), minor(stbuf->st_rdev));
973 } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
974 v9fs_string_sprintf(&v9stat->extension, "%s %u",
975 "HARDLINKCOUNT", stbuf->st_nlink);
976 }
977 }
978
979 str = strrchr(name->data, '/');
980 if (str) {
981 str += 1;
982 } else {
983 str = name->data;
984 }
985
986 v9fs_string_sprintf(&v9stat->name, "%s", str);
987
988 v9stat->size = 61 +
989 v9fs_string_size(&v9stat->name) +
990 v9fs_string_size(&v9stat->uid) +
991 v9fs_string_size(&v9stat->gid) +
992 v9fs_string_size(&v9stat->muid) +
993 v9fs_string_size(&v9stat->extension);
994 return 0;
995}
996
00ede4c2
SK
997#define P9_STATS_MODE 0x00000001ULL
998#define P9_STATS_NLINK 0x00000002ULL
999#define P9_STATS_UID 0x00000004ULL
1000#define P9_STATS_GID 0x00000008ULL
1001#define P9_STATS_RDEV 0x00000010ULL
1002#define P9_STATS_ATIME 0x00000020ULL
1003#define P9_STATS_MTIME 0x00000040ULL
1004#define P9_STATS_CTIME 0x00000080ULL
1005#define P9_STATS_INO 0x00000100ULL
1006#define P9_STATS_SIZE 0x00000200ULL
1007#define P9_STATS_BLOCKS 0x00000400ULL
1008
1009#define P9_STATS_BTIME 0x00000800ULL
1010#define P9_STATS_GEN 0x00001000ULL
1011#define P9_STATS_DATA_VERSION 0x00002000ULL
1012
1013#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
1014#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */
1015
1016
1017static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
1018 V9fsStatDotl *v9lstat)
1019{
1020 memset(v9lstat, 0, sizeof(*v9lstat));
1021
1022 v9lstat->st_mode = stbuf->st_mode;
1023 v9lstat->st_nlink = stbuf->st_nlink;
1024 v9lstat->st_uid = stbuf->st_uid;
1025 v9lstat->st_gid = stbuf->st_gid;
1026 v9lstat->st_rdev = stbuf->st_rdev;
1027 v9lstat->st_size = stbuf->st_size;
1028 v9lstat->st_blksize = stbuf->st_blksize;
1029 v9lstat->st_blocks = stbuf->st_blocks;
1030 v9lstat->st_atime_sec = stbuf->st_atime;
1031 v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
1032 v9lstat->st_mtime_sec = stbuf->st_mtime;
1033 v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
1034 v9lstat->st_ctime_sec = stbuf->st_ctime;
1035 v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
1036 /* Currently we only support BASIC fields in stat */
1037 v9lstat->st_result_mask = P9_STATS_BASIC;
1038
1039 stat_to_qid(stbuf, &v9lstat->qid);
1040}
1041
1f5a89bf
AL
1042static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt)
1043{
1044 while (len && *iovcnt) {
1045 if (len < sg->iov_len) {
1046 sg->iov_len -= len;
1047 sg->iov_base += len;
1048 len = 0;
1049 } else {
1050 len -= sg->iov_len;
1051 sg++;
1052 *iovcnt -= 1;
1053 }
1054 }
1055
1056 return sg;
1057}
1058
1059static struct iovec *cap_sg(struct iovec *sg, int cap, int *cnt)
1060{
1061 int i;
1062 int total = 0;
1063
1064 for (i = 0; i < *cnt; i++) {
1065 if ((total + sg[i].iov_len) > cap) {
1066 sg[i].iov_len -= ((total + sg[i].iov_len) - cap);
1067 i++;
1068 break;
1069 }
1070 total += sg[i].iov_len;
1071 }
1072
1073 *cnt = i;
1074
1075 return sg;
1076}
1077
1078static void print_sg(struct iovec *sg, int cnt)
1079{
1080 int i;
1081
1082 printf("sg[%d]: {", cnt);
1083 for (i = 0; i < cnt; i++) {
1084 if (i) {
1085 printf(", ");
1086 }
1087 printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len);
1088 }
1089 printf("}\n");
1090}
1091
8cf89e00
AL
1092static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len)
1093{
1094 V9fsString str;
1095 v9fs_string_init(&str);
1096 v9fs_string_copy(&str, dst);
1097 v9fs_string_sprintf(dst, "%s%s", src->data, str.data+len);
1098 v9fs_string_free(&str);
1099}
1100
9f107513
AL
1101static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
1102{
92c1ad03
AL
1103 V9fsString version;
1104 size_t offset = 7;
1105
5e94c103 1106 pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
92c1ad03 1107
84151514
MK
1108 if (!strcmp(version.data, "9P2000.u")) {
1109 s->proto_version = V9FS_PROTO_2000U;
1110 } else if (!strcmp(version.data, "9P2000.L")) {
1111 s->proto_version = V9FS_PROTO_2000L;
1112 } else {
92c1ad03 1113 v9fs_string_sprintf(&version, "unknown");
9f107513 1114 }
92c1ad03 1115
5e94c103 1116 offset += pdu_marshal(pdu, offset, "ds", s->msize, &version);
92c1ad03
AL
1117 complete_pdu(s, pdu, offset);
1118
1119 v9fs_string_free(&version);
9f107513
AL
1120}
1121
1122static void v9fs_attach(V9fsState *s, V9fsPDU *pdu)
1123{
955efc47
AL
1124 int32_t fid, afid, n_uname;
1125 V9fsString uname, aname;
1126 V9fsFidState *fidp;
1127 V9fsQID qid;
1128 size_t offset = 7;
1129 ssize_t err;
1130
1131 pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname);
1132
1133 fidp = alloc_fid(s, fid);
1134 if (fidp == NULL) {
1135 err = -EINVAL;
1136 goto out;
9f107513 1137 }
955efc47
AL
1138
1139 fidp->uid = n_uname;
1140
1141 v9fs_string_sprintf(&fidp->path, "%s", "/");
1142 err = fid_to_qid(s, fidp, &qid);
1143 if (err) {
1144 err = -EINVAL;
1145 free_fid(s, fid);
1146 goto out;
1147 }
1148
1149 offset += pdu_marshal(pdu, offset, "Q", &qid);
1150
1151 err = offset;
1152out:
1153 complete_pdu(s, pdu, err);
1154 v9fs_string_free(&uname);
1155 v9fs_string_free(&aname);
9f107513
AL
1156}
1157
4da7d3fa
AL
1158static void v9fs_stat_post_lstat(V9fsState *s, V9fsStatState *vs, int err)
1159{
1160 if (err == -1) {
1161 err = -errno;
1162 goto out;
1163 }
1164
1165 err = stat_to_v9stat(s, &vs->fidp->path, &vs->stbuf, &vs->v9stat);
1166 if (err) {
1167 goto out;
1168 }
1169 vs->offset += pdu_marshal(vs->pdu, vs->offset, "wS", 0, &vs->v9stat);
1170 err = vs->offset;
1171
1172out:
1173 complete_pdu(s, vs->pdu, err);
1174 v9fs_stat_free(&vs->v9stat);
1175 qemu_free(vs);
1176}
1177
9f107513
AL
1178static void v9fs_stat(V9fsState *s, V9fsPDU *pdu)
1179{
4da7d3fa
AL
1180 int32_t fid;
1181 V9fsStatState *vs;
1182 ssize_t err = 0;
1183
1184 vs = qemu_malloc(sizeof(*vs));
1185 vs->pdu = pdu;
1186 vs->offset = 7;
1187
1188 memset(&vs->v9stat, 0, sizeof(vs->v9stat));
1189
1190 pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
1191
1192 vs->fidp = lookup_fid(s, fid);
1193 if (vs->fidp == NULL) {
1194 err = -ENOENT;
1195 goto out;
9f107513 1196 }
4da7d3fa
AL
1197
1198 err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
1199 v9fs_stat_post_lstat(s, vs, err);
1200 return;
1201
1202out:
1203 complete_pdu(s, vs->pdu, err);
1204 v9fs_stat_free(&vs->v9stat);
1205 qemu_free(vs);
9f107513
AL
1206}
1207
00ede4c2
SK
1208static void v9fs_getattr_post_lstat(V9fsState *s, V9fsStatStateDotl *vs,
1209 int err)
1210{
1211 if (err == -1) {
1212 err = -errno;
1213 goto out;
1214 }
1215
1216 stat_to_v9stat_dotl(s, &vs->stbuf, &vs->v9stat_dotl);
1217 vs->offset += pdu_marshal(vs->pdu, vs->offset, "A", &vs->v9stat_dotl);
1218 err = vs->offset;
1219
1220out:
1221 complete_pdu(s, vs->pdu, err);
1222 qemu_free(vs);
1223}
1224
1225static void v9fs_getattr(V9fsState *s, V9fsPDU *pdu)
1226{
1227 int32_t fid;
1228 V9fsStatStateDotl *vs;
1229 ssize_t err = 0;
1230 V9fsFidState *fidp;
1231 uint64_t request_mask;
1232
1233 vs = qemu_malloc(sizeof(*vs));
1234 vs->pdu = pdu;
1235 vs->offset = 7;
1236
1237 memset(&vs->v9stat_dotl, 0, sizeof(vs->v9stat_dotl));
1238
1239 pdu_unmarshal(vs->pdu, vs->offset, "dq", &fid, &request_mask);
1240
1241 fidp = lookup_fid(s, fid);
1242 if (fidp == NULL) {
1243 err = -ENOENT;
1244 goto out;
1245 }
1246
1247 /* Currently we only support BASIC fields in stat, so there is no
1248 * need to look at request_mask.
1249 */
1250 err = v9fs_do_lstat(s, &fidp->path, &vs->stbuf);
1251 v9fs_getattr_post_lstat(s, vs, err);
1252 return;
1253
1254out:
1255 complete_pdu(s, vs->pdu, err);
1256 qemu_free(vs);
1257}
1258
c79ce737
SK
1259/* From Linux kernel code */
1260#define ATTR_MODE (1 << 0)
1261#define ATTR_UID (1 << 1)
1262#define ATTR_GID (1 << 2)
1263#define ATTR_SIZE (1 << 3)
1264#define ATTR_ATIME (1 << 4)
1265#define ATTR_MTIME (1 << 5)
1266#define ATTR_CTIME (1 << 6)
1267#define ATTR_MASK 127
1268#define ATTR_ATIME_SET (1 << 7)
1269#define ATTR_MTIME_SET (1 << 8)
1270
1271static void v9fs_setattr_post_truncate(V9fsState *s, V9fsSetattrState *vs,
1272 int err)
1273{
1274 if (err == -1) {
1275 err = -errno;
1276 goto out;
1277 }
1278 err = vs->offset;
1279
1280out:
1281 complete_pdu(s, vs->pdu, err);
1282 qemu_free(vs);
1283}
1284
1285static void v9fs_setattr_post_chown(V9fsState *s, V9fsSetattrState *vs, int err)
1286{
1287 if (err == -1) {
1288 err = -errno;
1289 goto out;
1290 }
1291
1292 if (vs->v9iattr.valid & (ATTR_SIZE)) {
1293 err = v9fs_do_truncate(s, &vs->fidp->path, vs->v9iattr.size);
1294 }
1295 v9fs_setattr_post_truncate(s, vs, err);
1296 return;
1297
1298out:
1299 complete_pdu(s, vs->pdu, err);
1300 qemu_free(vs);
1301}
1302
1303static void v9fs_setattr_post_utimensat(V9fsState *s, V9fsSetattrState *vs,
1304 int err)
1305{
1306 if (err == -1) {
1307 err = -errno;
1308 goto out;
1309 }
1310
1311 /* If the only valid entry in iattr is ctime we can call
1312 * chown(-1,-1) to update the ctime of the file
1313 */
1314 if ((vs->v9iattr.valid & (ATTR_UID | ATTR_GID)) ||
1315 ((vs->v9iattr.valid & ATTR_CTIME)
1316 && !((vs->v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
1317 if (!(vs->v9iattr.valid & ATTR_UID)) {
1318 vs->v9iattr.uid = -1;
1319 }
1320 if (!(vs->v9iattr.valid & ATTR_GID)) {
1321 vs->v9iattr.gid = -1;
1322 }
1323 err = v9fs_do_chown(s, &vs->fidp->path, vs->v9iattr.uid,
1324 vs->v9iattr.gid);
1325 }
1326 v9fs_setattr_post_chown(s, vs, err);
1327 return;
1328
1329out:
1330 complete_pdu(s, vs->pdu, err);
1331 qemu_free(vs);
1332}
1333
1334static void v9fs_setattr_post_chmod(V9fsState *s, V9fsSetattrState *vs, int err)
1335{
1336 if (err == -1) {
1337 err = -errno;
1338 goto out;
1339 }
1340
1341 if (vs->v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) {
1342 struct timespec times[2];
1343 if (vs->v9iattr.valid & ATTR_ATIME) {
1344 if (vs->v9iattr.valid & ATTR_ATIME_SET) {
1345 times[0].tv_sec = vs->v9iattr.atime_sec;
1346 times[0].tv_nsec = vs->v9iattr.atime_nsec;
1347 } else {
1348 times[0].tv_nsec = UTIME_NOW;
1349 }
1350 } else {
1351 times[0].tv_nsec = UTIME_OMIT;
1352 }
1353
1354 if (vs->v9iattr.valid & ATTR_MTIME) {
1355 if (vs->v9iattr.valid & ATTR_MTIME_SET) {
1356 times[1].tv_sec = vs->v9iattr.mtime_sec;
1357 times[1].tv_nsec = vs->v9iattr.mtime_nsec;
1358 } else {
1359 times[1].tv_nsec = UTIME_NOW;
1360 }
1361 } else {
1362 times[1].tv_nsec = UTIME_OMIT;
1363 }
1364 err = v9fs_do_utimensat(s, &vs->fidp->path, times);
1365 }
1366 v9fs_setattr_post_utimensat(s, vs, err);
1367 return;
1368
1369out:
1370 complete_pdu(s, vs->pdu, err);
1371 qemu_free(vs);
1372}
1373
1374static void v9fs_setattr(V9fsState *s, V9fsPDU *pdu)
1375{
1376 int32_t fid;
1377 V9fsSetattrState *vs;
1378 int err = 0;
1379
1380 vs = qemu_malloc(sizeof(*vs));
1381 vs->pdu = pdu;
1382 vs->offset = 7;
1383
1384 pdu_unmarshal(pdu, vs->offset, "dI", &fid, &vs->v9iattr);
1385
1386 vs->fidp = lookup_fid(s, fid);
1387 if (vs->fidp == NULL) {
1388 err = -EINVAL;
1389 goto out;
1390 }
1391
1392 if (vs->v9iattr.valid & ATTR_MODE) {
1393 err = v9fs_do_chmod(s, &vs->fidp->path, vs->v9iattr.mode);
1394 }
1395
1396 v9fs_setattr_post_chmod(s, vs, err);
1397 return;
1398
1399out:
1400 complete_pdu(s, vs->pdu, err);
1401 qemu_free(vs);
1402}
1403
ff5e54c9
AL
1404static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
1405{
1406 complete_pdu(s, vs->pdu, err);
1407
1408 if (vs->nwnames) {
1409 for (vs->name_idx = 0; vs->name_idx < vs->nwnames; vs->name_idx++) {
1410 v9fs_string_free(&vs->wnames[vs->name_idx]);
1411 }
1412
1413 qemu_free(vs->wnames);
1414 qemu_free(vs->qids);
1415 }
1416}
1417
1418static void v9fs_walk_marshal(V9fsWalkState *vs)
1419{
1420 int i;
1421 vs->offset = 7;
1422 vs->offset += pdu_marshal(vs->pdu, vs->offset, "w", vs->nwnames);
1423
1424 for (i = 0; i < vs->nwnames; i++) {
1425 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qids[i]);
1426 }
1427}
1428
1429static void v9fs_walk_post_newfid_lstat(V9fsState *s, V9fsWalkState *vs,
1430 int err)
1431{
1432 if (err == -1) {
1433 free_fid(s, vs->newfidp->fid);
1434 v9fs_string_free(&vs->path);
1435 err = -ENOENT;
1436 goto out;
1437 }
1438
1439 stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
1440
1441 vs->name_idx++;
1442 if (vs->name_idx < vs->nwnames) {
1443 v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
1444 vs->wnames[vs->name_idx].data);
1445 v9fs_string_copy(&vs->newfidp->path, &vs->path);
1446
1447 err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
1448 v9fs_walk_post_newfid_lstat(s, vs, err);
1449 return;
1450 }
1451
1452 v9fs_string_free(&vs->path);
1453 v9fs_walk_marshal(vs);
1454 err = vs->offset;
1455out:
1456 v9fs_walk_complete(s, vs, err);
1457}
1458
1459static void v9fs_walk_post_oldfid_lstat(V9fsState *s, V9fsWalkState *vs,
1460 int err)
1461{
1462 if (err == -1) {
1463 v9fs_string_free(&vs->path);
1464 err = -ENOENT;
1465 goto out;
1466 }
1467
1468 stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
1469 vs->name_idx++;
1470 if (vs->name_idx < vs->nwnames) {
1471
1472 v9fs_string_sprintf(&vs->path, "%s/%s",
1473 vs->fidp->path.data, vs->wnames[vs->name_idx].data);
1474 v9fs_string_copy(&vs->fidp->path, &vs->path);
1475
1476 err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
1477 v9fs_walk_post_oldfid_lstat(s, vs, err);
1478 return;
1479 }
1480
1481 v9fs_string_free(&vs->path);
1482 v9fs_walk_marshal(vs);
1483 err = vs->offset;
1484out:
1485 v9fs_walk_complete(s, vs, err);
1486}
1487
9f107513
AL
1488static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
1489{
ff5e54c9
AL
1490 int32_t fid, newfid;
1491 V9fsWalkState *vs;
1492 int err = 0;
1493 int i;
1494
1495 vs = qemu_malloc(sizeof(*vs));
1496 vs->pdu = pdu;
1497 vs->wnames = NULL;
1498 vs->qids = NULL;
1499 vs->offset = 7;
1500
1501 vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "ddw", &fid,
1502 &newfid, &vs->nwnames);
1503
1504 if (vs->nwnames) {
1505 vs->wnames = qemu_mallocz(sizeof(vs->wnames[0]) * vs->nwnames);
1506
1507 vs->qids = qemu_mallocz(sizeof(vs->qids[0]) * vs->nwnames);
1508
1509 for (i = 0; i < vs->nwnames; i++) {
1510 vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "s",
1511 &vs->wnames[i]);
1512 }
1513 }
1514
1515 vs->fidp = lookup_fid(s, fid);
1516 if (vs->fidp == NULL) {
1517 err = -ENOENT;
1518 goto out;
1519 }
1520
1521 /* FIXME: is this really valid? */
1522 if (fid == newfid) {
1523
d62dbb51 1524 BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
ff5e54c9
AL
1525 v9fs_string_init(&vs->path);
1526 vs->name_idx = 0;
1527
1528 if (vs->name_idx < vs->nwnames) {
1529 v9fs_string_sprintf(&vs->path, "%s/%s",
1530 vs->fidp->path.data, vs->wnames[vs->name_idx].data);
1531 v9fs_string_copy(&vs->fidp->path, &vs->path);
1532
1533 err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
1534 v9fs_walk_post_oldfid_lstat(s, vs, err);
1535 return;
1536 }
1537 } else {
1538 vs->newfidp = alloc_fid(s, newfid);
1539 if (vs->newfidp == NULL) {
1540 err = -EINVAL;
1541 goto out;
1542 }
1543
1544 vs->newfidp->uid = vs->fidp->uid;
1545 v9fs_string_init(&vs->path);
1546 vs->name_idx = 0;
1547 v9fs_string_copy(&vs->newfidp->path, &vs->fidp->path);
1548
1549 if (vs->name_idx < vs->nwnames) {
1550 v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
1551 vs->wnames[vs->name_idx].data);
1552 v9fs_string_copy(&vs->newfidp->path, &vs->path);
1553
1554 err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
1555 v9fs_walk_post_newfid_lstat(s, vs, err);
1556 return;
1557 }
9f107513 1558 }
ff5e54c9
AL
1559
1560 v9fs_walk_marshal(vs);
1561 err = vs->offset;
1562out:
1563 v9fs_walk_complete(s, vs, err);
9f107513
AL
1564}
1565
5e94c103
MK
1566static int32_t get_iounit(V9fsState *s, V9fsString *name)
1567{
1568 struct statfs stbuf;
1569 int32_t iounit = 0;
1570
1571 /*
1572 * iounit should be multiples of f_bsize (host filesystem block size
1573 * and as well as less than (client msize - P9_IOHDRSZ))
1574 */
1575 if (!v9fs_do_statfs(s, name, &stbuf)) {
1576 iounit = stbuf.f_bsize;
1577 iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
1578 }
1579
1580 if (!iounit) {
1581 iounit = s->msize - P9_IOHDRSZ;
1582 }
1583 return iounit;
1584}
1585
a6568fe2
AL
1586static void v9fs_open_post_opendir(V9fsState *s, V9fsOpenState *vs, int err)
1587{
d62dbb51 1588 if (vs->fidp->fs.dir == NULL) {
a6568fe2
AL
1589 err = -errno;
1590 goto out;
1591 }
d62dbb51 1592 vs->fidp->fid_type = P9_FID_DIR;
a6568fe2
AL
1593 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
1594 err = vs->offset;
1595out:
1596 complete_pdu(s, vs->pdu, err);
1597 qemu_free(vs);
1598
1599}
1600
5e94c103
MK
1601static void v9fs_open_post_getiounit(V9fsState *s, V9fsOpenState *vs)
1602{
1603 int err;
1604 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
1605 err = vs->offset;
1606 complete_pdu(s, vs->pdu, err);
1607 qemu_free(vs);
1608}
1609
a6568fe2
AL
1610static void v9fs_open_post_open(V9fsState *s, V9fsOpenState *vs, int err)
1611{
d62dbb51 1612 if (vs->fidp->fs.fd == -1) {
a6568fe2
AL
1613 err = -errno;
1614 goto out;
1615 }
d62dbb51 1616 vs->fidp->fid_type = P9_FID_FILE;
5e94c103
MK
1617 vs->iounit = get_iounit(s, &vs->fidp->path);
1618 v9fs_open_post_getiounit(s, vs);
1619 return;
a6568fe2
AL
1620out:
1621 complete_pdu(s, vs->pdu, err);
1622 qemu_free(vs);
1623}
1624
771e9d4c
MK
1625static inline int valid_flags(int flag)
1626{
1627 if (flag & O_NOCTTY || flag & O_NONBLOCK || flag & O_ASYNC ||
1628 flag & O_CLOEXEC)
1629 return 0;
1630 else
1631 return 1;
1632}
1633
a6568fe2
AL
1634static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
1635{
771e9d4c
MK
1636 int flags;
1637
a6568fe2
AL
1638 if (err) {
1639 err = -errno;
1640 goto out;
1641 }
1642
1643 stat_to_qid(&vs->stbuf, &vs->qid);
1644
1645 if (S_ISDIR(vs->stbuf.st_mode)) {
d62dbb51 1646 vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fidp->path);
a6568fe2
AL
1647 v9fs_open_post_opendir(s, vs, err);
1648 } else {
771e9d4c
MK
1649 if (s->proto_version == V9FS_PROTO_2000L) {
1650 if (!valid_flags(vs->mode)) {
1651 err = -EINVAL;
1652 goto out;
1653 }
1654 flags = vs->mode;
1655 } else {
1656 flags = omode_to_uflags(vs->mode);
1657 }
d62dbb51 1658 vs->fidp->fs.fd = v9fs_do_open(s, &vs->fidp->path, flags);
a6568fe2
AL
1659 v9fs_open_post_open(s, vs, err);
1660 }
1661 return;
1662out:
1663 complete_pdu(s, vs->pdu, err);
1664 qemu_free(vs);
9f107513
AL
1665}
1666
1667static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
a6568fe2
AL
1668{
1669 int32_t fid;
1670 V9fsOpenState *vs;
1671 ssize_t err = 0;
1672
a6568fe2
AL
1673 vs = qemu_malloc(sizeof(*vs));
1674 vs->pdu = pdu;
1675 vs->offset = 7;
771e9d4c 1676 vs->mode = 0;
a6568fe2 1677
771e9d4c
MK
1678 if (s->proto_version == V9FS_PROTO_2000L) {
1679 pdu_unmarshal(vs->pdu, vs->offset, "dd", &fid, &vs->mode);
1680 } else {
1681 pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
1682 }
a6568fe2
AL
1683
1684 vs->fidp = lookup_fid(s, fid);
1685 if (vs->fidp == NULL) {
1686 err = -ENOENT;
1687 goto out;
1688 }
1689
d62dbb51 1690 BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
a6568fe2
AL
1691
1692 err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
1693
1694 v9fs_open_post_lstat(s, vs, err);
1695 return;
1696out:
1697 complete_pdu(s, pdu, err);
1698 qemu_free(vs);
1699}
1700
c1568af5
VJ
1701static void v9fs_post_lcreate(V9fsState *s, V9fsLcreateState *vs, int err)
1702{
1703 if (err == 0) {
1704 v9fs_string_copy(&vs->fidp->path, &vs->fullname);
1705 stat_to_qid(&vs->stbuf, &vs->qid);
1706 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid,
1707 &vs->iounit);
1708 err = vs->offset;
1709 } else {
d62dbb51
AK
1710 vs->fidp->fid_type = P9_FID_NONE;
1711 close(vs->fidp->fs.fd);
c1568af5
VJ
1712 err = -errno;
1713 }
1714
1715 complete_pdu(s, vs->pdu, err);
1716 v9fs_string_free(&vs->name);
1717 v9fs_string_free(&vs->fullname);
1718 qemu_free(vs);
1719}
1720
1721static void v9fs_lcreate_post_get_iounit(V9fsState *s, V9fsLcreateState *vs,
1722 int err)
1723{
1724 if (err) {
1725 err = -errno;
1726 goto out;
1727 }
1728 err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
1729
1730out:
1731 v9fs_post_lcreate(s, vs, err);
1732}
1733
1734static void v9fs_lcreate_post_do_open2(V9fsState *s, V9fsLcreateState *vs,
1735 int err)
1736{
d62dbb51 1737 if (vs->fidp->fs.fd == -1) {
c1568af5
VJ
1738 err = -errno;
1739 goto out;
1740 }
d62dbb51 1741 vs->fidp->fid_type = P9_FID_FILE;
c1568af5
VJ
1742 vs->iounit = get_iounit(s, &vs->fullname);
1743 v9fs_lcreate_post_get_iounit(s, vs, err);
1744 return;
1745
1746out:
1747 v9fs_post_lcreate(s, vs, err);
1748}
1749
1750static void v9fs_lcreate(V9fsState *s, V9fsPDU *pdu)
1751{
1752 int32_t dfid, flags, mode;
1753 gid_t gid;
1754 V9fsLcreateState *vs;
1755 ssize_t err = 0;
1756
1757 vs = qemu_malloc(sizeof(*vs));
1758 vs->pdu = pdu;
1759 vs->offset = 7;
1760
1761 v9fs_string_init(&vs->fullname);
1762
1763 pdu_unmarshal(vs->pdu, vs->offset, "dsddd", &dfid, &vs->name, &flags,
1764 &mode, &gid);
1765
1766 vs->fidp = lookup_fid(s, dfid);
1767 if (vs->fidp == NULL) {
1768 err = -ENOENT;
1769 goto out;
1770 }
1771
1772 v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
1773 vs->name.data);
1774
d62dbb51 1775 vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
c1568af5
VJ
1776 gid, flags, mode);
1777 v9fs_lcreate_post_do_open2(s, vs, err);
1778 return;
1779
1780out:
1781 complete_pdu(s, vs->pdu, err);
1782 v9fs_string_free(&vs->name);
1783 qemu_free(vs);
1784}
1785
a6568fe2
AL
1786static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
1787{
bbd5697b
AL
1788 int32_t fid;
1789 size_t offset = 7;
1790 int err;
1791
1792 pdu_unmarshal(pdu, offset, "d", &fid);
1793
1794 err = free_fid(s, fid);
1795 if (err < 0) {
1796 goto out;
a6568fe2 1797 }
bbd5697b
AL
1798
1799 offset = 7;
1800 err = offset;
1801out:
1802 complete_pdu(s, pdu, err);
9f107513
AL
1803}
1804
a9231555
AL
1805static void v9fs_read_post_readdir(V9fsState *, V9fsReadState *, ssize_t);
1806
1807static void v9fs_read_post_seekdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
1808{
1809 if (err) {
1810 goto out;
1811 }
1812 v9fs_stat_free(&vs->v9stat);
1813 v9fs_string_free(&vs->name);
1814 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
1815 vs->offset += vs->count;
1816 err = vs->offset;
1817out:
1818 complete_pdu(s, vs->pdu, err);
1819 qemu_free(vs);
1820 return;
1821}
1822
1823static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
1824 ssize_t err)
1825{
1826 if (err) {
1827 err = -errno;
1828 goto out;
1829 }
1830 err = stat_to_v9stat(s, &vs->name, &vs->stbuf, &vs->v9stat);
1831 if (err) {
1832 goto out;
1833 }
1834
1835 vs->len = pdu_marshal(vs->pdu, vs->offset + 4 + vs->count, "S",
1836 &vs->v9stat);
1837 if ((vs->len != (vs->v9stat.size + 2)) ||
1838 ((vs->count + vs->len) > vs->max_count)) {
d62dbb51 1839 v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
a9231555
AL
1840 v9fs_read_post_seekdir(s, vs, err);
1841 return;
1842 }
1843 vs->count += vs->len;
1844 v9fs_stat_free(&vs->v9stat);
1845 v9fs_string_free(&vs->name);
1846 vs->dir_pos = vs->dent->d_off;
d62dbb51 1847 vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
a9231555
AL
1848 v9fs_read_post_readdir(s, vs, err);
1849 return;
1850out:
d62dbb51 1851 v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
a9231555
AL
1852 v9fs_read_post_seekdir(s, vs, err);
1853 return;
1854
1855}
1856
1857static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
1858{
1859 if (vs->dent) {
1860 memset(&vs->v9stat, 0, sizeof(vs->v9stat));
1861 v9fs_string_init(&vs->name);
1862 v9fs_string_sprintf(&vs->name, "%s/%s", vs->fidp->path.data,
1863 vs->dent->d_name);
1864 err = v9fs_do_lstat(s, &vs->name, &vs->stbuf);
1865 v9fs_read_post_dir_lstat(s, vs, err);
1866 return;
1867 }
1868
1869 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
1870 vs->offset += vs->count;
1871 err = vs->offset;
1872 complete_pdu(s, vs->pdu, err);
1873 qemu_free(vs);
1874 return;
1875}
1876
1877static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
1878{
d62dbb51 1879 vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
a9231555
AL
1880 v9fs_read_post_readdir(s, vs, err);
1881 return;
1882}
1883
1884static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs,
1885 ssize_t err)
1886{
d62dbb51 1887 vs->dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
a9231555
AL
1888 v9fs_read_post_telldir(s, vs, err);
1889 return;
1890}
1891
1892static void v9fs_read_post_readv(V9fsState *s, V9fsReadState *vs, ssize_t err)
1893{
1894 if (err < 0) {
1895 /* IO error return the error */
1896 err = -errno;
1897 goto out;
1898 }
1899 vs->total += vs->len;
1900 vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
1901 if (vs->total < vs->count && vs->len > 0) {
1902 do {
1903 if (0) {
1904 print_sg(vs->sg, vs->cnt);
1905 }
d62dbb51 1906 vs->len = v9fs_do_readv(s, vs->fidp->fs.fd, vs->sg, vs->cnt);
a9231555
AL
1907 } while (vs->len == -1 && errno == EINTR);
1908 if (vs->len == -1) {
1909 err = -errno;
1910 }
1911 v9fs_read_post_readv(s, vs, err);
1912 return;
1913 }
1914 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
1915 vs->offset += vs->count;
1916 err = vs->offset;
1917
1918out:
1919 complete_pdu(s, vs->pdu, err);
1920 qemu_free(vs);
1921}
1922
1923static void v9fs_read_post_lseek(V9fsState *s, V9fsReadState *vs, ssize_t err)
1924{
1925 if (err == -1) {
1926 err = -errno;
1927 goto out;
1928 }
1929 vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
1930
1931 if (vs->total < vs->count) {
1932 do {
1933 if (0) {
1934 print_sg(vs->sg, vs->cnt);
1935 }
d62dbb51 1936 vs->len = v9fs_do_readv(s, vs->fidp->fs.fd, vs->sg, vs->cnt);
a9231555
AL
1937 } while (vs->len == -1 && errno == EINTR);
1938 if (vs->len == -1) {
1939 err = -errno;
1940 }
1941 v9fs_read_post_readv(s, vs, err);
1942 return;
1943 }
1944out:
1945 complete_pdu(s, vs->pdu, err);
1946 qemu_free(vs);
1947}
1948
9f107513
AL
1949static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
1950{
a9231555
AL
1951 int32_t fid;
1952 V9fsReadState *vs;
1953 ssize_t err = 0;
1954
1955 vs = qemu_malloc(sizeof(*vs));
1956 vs->pdu = pdu;
1957 vs->offset = 7;
1958 vs->total = 0;
1959 vs->len = 0;
1960 vs->count = 0;
1961
1962 pdu_unmarshal(vs->pdu, vs->offset, "dqd", &fid, &vs->off, &vs->count);
1963
1964 vs->fidp = lookup_fid(s, fid);
1965 if (vs->fidp == NULL) {
1966 err = -EINVAL;
1967 goto out;
1968 }
1969
d62dbb51 1970 if (vs->fidp->fs.dir) {
a9231555
AL
1971 vs->max_count = vs->count;
1972 vs->count = 0;
1973 if (vs->off == 0) {
d62dbb51 1974 v9fs_do_rewinddir(s, vs->fidp->fs.dir);
a9231555
AL
1975 }
1976 v9fs_read_post_rewinddir(s, vs, err);
1977 return;
d62dbb51 1978 } else if (vs->fidp->fs.fd != -1) {
a9231555
AL
1979 vs->sg = vs->iov;
1980 pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
d62dbb51 1981 err = v9fs_do_lseek(s, vs->fidp->fs.fd, vs->off, SEEK_SET);
a9231555
AL
1982 v9fs_read_post_lseek(s, vs, err);
1983 return;
1984 } else {
1985 err = -EINVAL;
9f107513 1986 }
a9231555
AL
1987out:
1988 complete_pdu(s, pdu, err);
1989 qemu_free(vs);
9f107513
AL
1990}
1991
c18e2f94
SK
1992typedef struct V9fsReadDirState {
1993 V9fsPDU *pdu;
1994 V9fsFidState *fidp;
1995 V9fsQID qid;
1996 off_t saved_dir_pos;
1997 struct dirent *dent;
1998 int32_t count;
1999 int32_t max_count;
2000 size_t offset;
2001 int64_t initial_offset;
2002 V9fsString name;
2003} V9fsReadDirState;
2004
2005static void v9fs_readdir_post_seekdir(V9fsState *s, V9fsReadDirState *vs)
2006{
2007 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
2008 vs->offset += vs->count;
2009 complete_pdu(s, vs->pdu, vs->offset);
2010 qemu_free(vs);
2011 return;
2012}
2013
2014/* Size of each dirent on the wire: size of qid (13) + size of offset (8)
2015 * size of type (1) + size of name.size (2) + strlen(name.data)
2016 */
2017#define V9_READDIR_DATA_SZ (24 + strlen(vs->name.data))
2018
2019static void v9fs_readdir_post_readdir(V9fsState *s, V9fsReadDirState *vs)
2020{
2021 int len;
2022 size_t size;
2023
2024 if (vs->dent) {
2025 v9fs_string_init(&vs->name);
2026 v9fs_string_sprintf(&vs->name, "%s", vs->dent->d_name);
2027
2028 if ((vs->count + V9_READDIR_DATA_SZ) > vs->max_count) {
2029 /* Ran out of buffer. Set dir back to old position and return */
d62dbb51 2030 v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->saved_dir_pos);
c18e2f94
SK
2031 v9fs_readdir_post_seekdir(s, vs);
2032 return;
2033 }
2034
2035 /* Fill up just the path field of qid because the client uses
2036 * only that. To fill the entire qid structure we will have
2037 * to stat each dirent found, which is expensive
2038 */
2039 size = MIN(sizeof(vs->dent->d_ino), sizeof(vs->qid.path));
2040 memcpy(&vs->qid.path, &vs->dent->d_ino, size);
2041 /* Fill the other fields with dummy values */
2042 vs->qid.type = 0;
2043 vs->qid.version = 0;
2044
2045 len = pdu_marshal(vs->pdu, vs->offset+4+vs->count, "Qqbs",
2046 &vs->qid, vs->dent->d_off,
2047 vs->dent->d_type, &vs->name);
2048 vs->count += len;
2049 v9fs_string_free(&vs->name);
2050 vs->saved_dir_pos = vs->dent->d_off;
d62dbb51 2051 vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
c18e2f94
SK
2052 v9fs_readdir_post_readdir(s, vs);
2053 return;
2054 }
2055
2056 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
2057 vs->offset += vs->count;
2058 complete_pdu(s, vs->pdu, vs->offset);
2059 qemu_free(vs);
2060 return;
2061}
2062
2063static void v9fs_readdir_post_telldir(V9fsState *s, V9fsReadDirState *vs)
2064{
d62dbb51 2065 vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
c18e2f94
SK
2066 v9fs_readdir_post_readdir(s, vs);
2067 return;
2068}
2069
2070static void v9fs_readdir_post_setdir(V9fsState *s, V9fsReadDirState *vs)
2071{
d62dbb51 2072 vs->saved_dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
c18e2f94
SK
2073 v9fs_readdir_post_telldir(s, vs);
2074 return;
2075}
2076
2077static void v9fs_readdir(V9fsState *s, V9fsPDU *pdu)
2078{
2079 int32_t fid;
2080 V9fsReadDirState *vs;
2081 ssize_t err = 0;
2082 size_t offset = 7;
2083
2084 vs = qemu_malloc(sizeof(*vs));
2085 vs->pdu = pdu;
2086 vs->offset = 7;
2087 vs->count = 0;
2088
2089 pdu_unmarshal(vs->pdu, offset, "dqd", &fid, &vs->initial_offset,
2090 &vs->max_count);
2091
2092 vs->fidp = lookup_fid(s, fid);
d62dbb51 2093 if (vs->fidp == NULL || !(vs->fidp->fs.dir)) {
c18e2f94
SK
2094 err = -EINVAL;
2095 goto out;
2096 }
2097
2098 if (vs->initial_offset == 0) {
d62dbb51 2099 v9fs_do_rewinddir(s, vs->fidp->fs.dir);
c18e2f94 2100 } else {
d62dbb51 2101 v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->initial_offset);
c18e2f94
SK
2102 }
2103
2104 v9fs_readdir_post_setdir(s, vs);
2105 return;
2106
2107out:
2108 complete_pdu(s, pdu, err);
2109 qemu_free(vs);
2110 return;
2111}
2112
8449360c
AL
2113static void v9fs_write_post_writev(V9fsState *s, V9fsWriteState *vs,
2114 ssize_t err)
2115{
2116 if (err < 0) {
2117 /* IO error return the error */
2118 err = -errno;
2119 goto out;
2120 }
2121 vs->total += vs->len;
2122 vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
2123 if (vs->total < vs->count && vs->len > 0) {
2124 do {
2125 if (0) {
2126 print_sg(vs->sg, vs->cnt);
2127 }
d62dbb51 2128 vs->len = v9fs_do_writev(s, vs->fidp->fs.fd, vs->sg, vs->cnt);
8449360c
AL
2129 } while (vs->len == -1 && errno == EINTR);
2130 if (vs->len == -1) {
2131 err = -errno;
2132 }
2133 v9fs_write_post_writev(s, vs, err);
2134 return;
2135 }
2136 vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
2137
2138 err = vs->offset;
2139out:
2140 complete_pdu(s, vs->pdu, err);
2141 qemu_free(vs);
2142}
2143
2144static void v9fs_write_post_lseek(V9fsState *s, V9fsWriteState *vs, ssize_t err)
2145{
2146 if (err == -1) {
2147 err = -errno;
2148 goto out;
2149 }
2150 vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
2151
2152 if (vs->total < vs->count) {
2153 do {
2154 if (0) {
2155 print_sg(vs->sg, vs->cnt);
2156 }
d62dbb51 2157 vs->len = v9fs_do_writev(s, vs->fidp->fs.fd, vs->sg, vs->cnt);
8449360c
AL
2158 } while (vs->len == -1 && errno == EINTR);
2159 if (vs->len == -1) {
2160 err = -errno;
2161 }
2162 v9fs_write_post_writev(s, vs, err);
2163 return;
2164 }
2165
2166out:
2167 complete_pdu(s, vs->pdu, err);
2168 qemu_free(vs);
2169}
2170
9f107513
AL
2171static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
2172{
8449360c
AL
2173 int32_t fid;
2174 V9fsWriteState *vs;
2175 ssize_t err;
2176
2177 vs = qemu_malloc(sizeof(*vs));
2178
2179 vs->pdu = pdu;
2180 vs->offset = 7;
2181 vs->sg = vs->iov;
2182 vs->total = 0;
2183 vs->len = 0;
2184
2185 pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count,
2186 vs->sg, &vs->cnt);
2187
2188 vs->fidp = lookup_fid(s, fid);
2189 if (vs->fidp == NULL) {
2190 err = -EINVAL;
2191 goto out;
9f107513 2192 }
8449360c 2193
d62dbb51 2194 if (vs->fidp->fs.fd == -1) {
8449360c
AL
2195 err = -EINVAL;
2196 goto out;
2197 }
2198
d62dbb51 2199 err = v9fs_do_lseek(s, vs->fidp->fs.fd, vs->off, SEEK_SET);
8449360c
AL
2200
2201 v9fs_write_post_lseek(s, vs, err);
2202 return;
2203
2204out:
2205 complete_pdu(s, vs->pdu, err);
2206 qemu_free(vs);
9f107513
AL
2207}
2208
5e94c103 2209static void v9fs_create_post_getiounit(V9fsState *s, V9fsCreateState *vs)
c494dd6f 2210{
5e94c103
MK
2211 int err;
2212 v9fs_string_copy(&vs->fidp->path, &vs->fullname);
2213 stat_to_qid(&vs->stbuf, &vs->qid);
c494dd6f 2214
5e94c103
MK
2215 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
2216 err = vs->offset;
c494dd6f 2217
5e94c103
MK
2218 complete_pdu(s, vs->pdu, err);
2219 v9fs_string_free(&vs->name);
2220 v9fs_string_free(&vs->extension);
2221 v9fs_string_free(&vs->fullname);
2222 qemu_free(vs);
2223}
2224
2225static void v9fs_post_create(V9fsState *s, V9fsCreateState *vs, int err)
2226{
2227 if (err == 0) {
2228 vs->iounit = get_iounit(s, &vs->fidp->path);
2229 v9fs_create_post_getiounit(s, vs);
2230 return;
c494dd6f
AL
2231 }
2232
2233 complete_pdu(s, vs->pdu, err);
2234 v9fs_string_free(&vs->name);
2235 v9fs_string_free(&vs->extension);
2236 v9fs_string_free(&vs->fullname);
2237 qemu_free(vs);
2238}
2239
2240static void v9fs_create_post_perms(V9fsState *s, V9fsCreateState *vs, int err)
2241{
2242 if (err) {
2243 err = -errno;
2244 }
2245 v9fs_post_create(s, vs, err);
2246}
2247
2248static void v9fs_create_post_opendir(V9fsState *s, V9fsCreateState *vs,
2249 int err)
2250{
d62dbb51 2251 if (!vs->fidp->fs.dir) {
c494dd6f
AL
2252 err = -errno;
2253 }
d62dbb51 2254 vs->fidp->fid_type = P9_FID_DIR;
c494dd6f
AL
2255 v9fs_post_create(s, vs, err);
2256}
2257
2258static void v9fs_create_post_dir_lstat(V9fsState *s, V9fsCreateState *vs,
2259 int err)
2260{
2261 if (err) {
2262 err = -errno;
2263 goto out;
2264 }
2265
d62dbb51 2266 vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fullname);
c494dd6f
AL
2267 v9fs_create_post_opendir(s, vs, err);
2268 return;
2269
2270out:
2271 v9fs_post_create(s, vs, err);
2272}
2273
2274static void v9fs_create_post_mkdir(V9fsState *s, V9fsCreateState *vs, int err)
2275{
2276 if (err) {
2277 err = -errno;
2278 goto out;
2279 }
2280
2281 err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
2282 v9fs_create_post_dir_lstat(s, vs, err);
2283 return;
2284
2285out:
2286 v9fs_post_create(s, vs, err);
2287}
2288
c494dd6f
AL
2289static void v9fs_create_post_fstat(V9fsState *s, V9fsCreateState *vs, int err)
2290{
2291 if (err) {
d62dbb51
AK
2292 vs->fidp->fid_type = P9_FID_NONE;
2293 close(vs->fidp->fs.fd);
c494dd6f
AL
2294 err = -errno;
2295 }
c494dd6f
AL
2296 v9fs_post_create(s, vs, err);
2297 return;
2298}
2299
2300static void v9fs_create_post_open2(V9fsState *s, V9fsCreateState *vs, int err)
2301{
d62dbb51 2302 if (vs->fidp->fs.fd == -1) {
c494dd6f
AL
2303 err = -errno;
2304 goto out;
2305 }
d62dbb51
AK
2306 vs->fidp->fid_type = P9_FID_FILE;
2307 err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
c494dd6f
AL
2308 v9fs_create_post_fstat(s, vs, err);
2309
2310 return;
2311
2312out:
2313 v9fs_post_create(s, vs, err);
2314
2315}
2316
2317static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err)
2318{
2319
2320 if (err == 0 || errno != ENOENT) {
2321 err = -errno;
2322 goto out;
2323 }
2324
2325 if (vs->perm & P9_STAT_MODE_DIR) {
b67592ea
MK
2326 err = v9fs_do_mkdir(s, vs->fullname.data, vs->perm & 0777,
2327 vs->fidp->uid, -1);
c494dd6f
AL
2328 v9fs_create_post_mkdir(s, vs, err);
2329 } else if (vs->perm & P9_STAT_MODE_SYMLINK) {
08c60fc9
VJ
2330 err = v9fs_do_symlink(s, vs->fidp, vs->extension.data,
2331 vs->fullname.data, -1);
c494dd6f
AL
2332 v9fs_create_post_perms(s, vs, err);
2333 } else if (vs->perm & P9_STAT_MODE_LINK) {
2334 int32_t nfid = atoi(vs->extension.data);
2335 V9fsFidState *nfidp = lookup_fid(s, nfid);
2336 if (nfidp == NULL) {
2337 err = -errno;
2338 v9fs_post_create(s, vs, err);
2339 }
2340 err = v9fs_do_link(s, &nfidp->path, &vs->fullname);
2341 v9fs_create_post_perms(s, vs, err);
2342 } else if (vs->perm & P9_STAT_MODE_DEVICE) {
2343 char ctype;
2344 uint32_t major, minor;
2345 mode_t nmode = 0;
2346
2347 if (sscanf(vs->extension.data, "%c %u %u", &ctype, &major,
2348 &minor) != 3) {
2349 err = -errno;
2350 v9fs_post_create(s, vs, err);
2351 }
2352
2353 switch (ctype) {
2354 case 'c':
2355 nmode = S_IFCHR;
2356 break;
2357 case 'b':
2358 nmode = S_IFBLK;
2359 break;
2360 default:
2361 err = -EIO;
2362 v9fs_post_create(s, vs, err);
2363 }
2364
2365 nmode |= vs->perm & 0777;
5268cecc
MK
2366 err = v9fs_do_mknod(s, vs->fullname.data, nmode,
2367 makedev(major, minor), vs->fidp->uid, -1);
c494dd6f
AL
2368 v9fs_create_post_perms(s, vs, err);
2369 } else if (vs->perm & P9_STAT_MODE_NAMED_PIPE) {
5268cecc
MK
2370 err = v9fs_do_mknod(s, vs->fullname.data, S_IFIFO | (vs->perm & 0777),
2371 0, vs->fidp->uid, -1);
c494dd6f
AL
2372 v9fs_post_create(s, vs, err);
2373 } else if (vs->perm & P9_STAT_MODE_SOCKET) {
5268cecc
MK
2374 err = v9fs_do_mknod(s, vs->fullname.data, S_IFSOCK | (vs->perm & 0777),
2375 0, vs->fidp->uid, -1);
63729c36 2376 v9fs_post_create(s, vs, err);
c494dd6f 2377 } else {
d62dbb51 2378 vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
c1568af5
VJ
2379 -1, omode_to_uflags(vs->mode)|O_CREAT, vs->perm);
2380
c494dd6f
AL
2381 v9fs_create_post_open2(s, vs, err);
2382 }
2383
2384 return;
2385
2386out:
2387 v9fs_post_create(s, vs, err);
2388}
2389
9f107513
AL
2390static void v9fs_create(V9fsState *s, V9fsPDU *pdu)
2391{
c494dd6f
AL
2392 int32_t fid;
2393 V9fsCreateState *vs;
2394 int err = 0;
2395
2396 vs = qemu_malloc(sizeof(*vs));
2397 vs->pdu = pdu;
2398 vs->offset = 7;
2399
2400 v9fs_string_init(&vs->fullname);
2401
2402 pdu_unmarshal(vs->pdu, vs->offset, "dsdbs", &fid, &vs->name,
2403 &vs->perm, &vs->mode, &vs->extension);
2404
2405 vs->fidp = lookup_fid(s, fid);
2406 if (vs->fidp == NULL) {
2407 err = -EINVAL;
2408 goto out;
9f107513 2409 }
c494dd6f
AL
2410
2411 v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
2412 vs->name.data);
2413
2414 err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
2415 v9fs_create_post_lstat(s, vs, err);
2416 return;
2417
2418out:
2419 complete_pdu(s, vs->pdu, err);
2420 v9fs_string_free(&vs->name);
2421 v9fs_string_free(&vs->extension);
2422 qemu_free(vs);
9f107513
AL
2423}
2424
08c60fc9
VJ
2425static void v9fs_post_symlink(V9fsState *s, V9fsSymlinkState *vs, int err)
2426{
2427 if (err == 0) {
2428 stat_to_qid(&vs->stbuf, &vs->qid);
2429 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
2430 err = vs->offset;
2431 } else {
2432 err = -errno;
2433 }
2434 complete_pdu(s, vs->pdu, err);
2435 v9fs_string_free(&vs->name);
2436 v9fs_string_free(&vs->symname);
2437 v9fs_string_free(&vs->fullname);
2438 qemu_free(vs);
2439}
2440
2441static void v9fs_symlink_post_do_symlink(V9fsState *s, V9fsSymlinkState *vs,
2442 int err)
2443{
2444 if (err) {
2445 goto out;
2446 }
2447 err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
2448out:
2449 v9fs_post_symlink(s, vs, err);
2450}
2451
2452static void v9fs_symlink(V9fsState *s, V9fsPDU *pdu)
2453{
2454 int32_t dfid;
2455 V9fsSymlinkState *vs;
2456 int err = 0;
2457 gid_t gid;
2458
2459 vs = qemu_malloc(sizeof(*vs));
2460 vs->pdu = pdu;
2461 vs->offset = 7;
2462
2463 v9fs_string_init(&vs->fullname);
2464
2465 pdu_unmarshal(vs->pdu, vs->offset, "dssd", &dfid, &vs->name,
2466 &vs->symname, &gid);
2467
2468 vs->dfidp = lookup_fid(s, dfid);
2469 if (vs->dfidp == NULL) {
2470 err = -EINVAL;
2471 goto out;
2472 }
2473
2474 v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->dfidp->path.data,
2475 vs->name.data);
2476 err = v9fs_do_symlink(s, vs->dfidp, vs->symname.data,
2477 vs->fullname.data, gid);
2478 v9fs_symlink_post_do_symlink(s, vs, err);
2479 return;
2480
2481out:
2482 complete_pdu(s, vs->pdu, err);
2483 v9fs_string_free(&vs->name);
2484 v9fs_string_free(&vs->symname);
2485 qemu_free(vs);
2486}
2487
9f107513
AL
2488static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
2489{
9c5e9d89
AL
2490 /* A nop call with no return */
2491 complete_pdu(s, pdu, 7);
9f107513
AL
2492}
2493
b2c224be
VJ
2494static void v9fs_link(V9fsState *s, V9fsPDU *pdu)
2495{
2496 int32_t dfid, oldfid;
2497 V9fsFidState *dfidp, *oldfidp;
2498 V9fsString name, fullname;
2499 size_t offset = 7;
2500 int err = 0;
2501
2502 v9fs_string_init(&fullname);
2503
2504 pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
2505
2506 dfidp = lookup_fid(s, dfid);
2507 if (dfidp == NULL) {
2508 err = -errno;
2509 goto out;
2510 }
2511
2512 oldfidp = lookup_fid(s, oldfid);
2513 if (oldfidp == NULL) {
2514 err = -errno;
2515 goto out;
2516 }
2517
2518 v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data);
2519 err = offset;
2520 err = v9fs_do_link(s, &oldfidp->path, &fullname);
2521 if (err) {
2522 err = -errno;
2523 }
2524 v9fs_string_free(&fullname);
2525
2526out:
2527 v9fs_string_free(&name);
2528 complete_pdu(s, pdu, err);
2529}
2530
5bae1900
AL
2531static void v9fs_remove_post_remove(V9fsState *s, V9fsRemoveState *vs,
2532 int err)
2533{
5bae1900 2534 if (err < 0) {
926487b7
SK
2535 err = -errno;
2536 } else {
2537 err = vs->offset;
5bae1900
AL
2538 }
2539
926487b7
SK
2540 /* For TREMOVE we need to clunk the fid even on failed remove */
2541 free_fid(s, vs->fidp->fid);
2542
5bae1900
AL
2543 complete_pdu(s, vs->pdu, err);
2544 qemu_free(vs);
2545}
2546
9f107513
AL
2547static void v9fs_remove(V9fsState *s, V9fsPDU *pdu)
2548{
5bae1900
AL
2549 int32_t fid;
2550 V9fsRemoveState *vs;
2551 int err = 0;
2552
2553 vs = qemu_malloc(sizeof(*vs));
2554 vs->pdu = pdu;
2555 vs->offset = 7;
2556
2557 pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
2558
2559 vs->fidp = lookup_fid(s, fid);
2560 if (vs->fidp == NULL) {
2561 err = -EINVAL;
2562 goto out;
9f107513 2563 }
5bae1900
AL
2564
2565 err = v9fs_do_remove(s, &vs->fidp->path);
2566 v9fs_remove_post_remove(s, vs, err);
2567 return;
2568
2569out:
2570 complete_pdu(s, pdu, err);
2571 qemu_free(vs);
9f107513
AL
2572}
2573
8cf89e00
AL
2574static void v9fs_wstat_post_truncate(V9fsState *s, V9fsWstatState *vs, int err)
2575{
2576 if (err < 0) {
2577 goto out;
2578 }
2579
2580 err = vs->offset;
2581
2582out:
2583 v9fs_stat_free(&vs->v9stat);
2584 complete_pdu(s, vs->pdu, err);
2585 qemu_free(vs);
2586}
2587
2588static void v9fs_wstat_post_rename(V9fsState *s, V9fsWstatState *vs, int err)
2589{
2590 if (err < 0) {
2591 goto out;
2592 }
8cf89e00
AL
2593 if (vs->v9stat.length != -1) {
2594 if (v9fs_do_truncate(s, &vs->fidp->path, vs->v9stat.length) < 0) {
2595 err = -errno;
2596 }
2597 }
2598 v9fs_wstat_post_truncate(s, vs, err);
2599 return;
2600
2601out:
2602 v9fs_stat_free(&vs->v9stat);
2603 complete_pdu(s, vs->pdu, err);
2604 qemu_free(vs);
2605}
2606
c7b4b0b3 2607static int v9fs_complete_rename(V9fsState *s, V9fsRenameState *vs)
8cf89e00 2608{
c7b4b0b3
MK
2609 int err = 0;
2610 char *old_name, *new_name;
2611 char *end;
8cf89e00 2612
c7b4b0b3
MK
2613 if (vs->newdirfid != -1) {
2614 V9fsFidState *dirfidp;
2615 dirfidp = lookup_fid(s, vs->newdirfid);
2616
2617 if (dirfidp == NULL) {
2618 err = -ENOENT;
2619 goto out;
2620 }
2621
d62dbb51 2622 BUG_ON(dirfidp->fid_type != P9_FID_NONE);
8cf89e00 2623
c7b4b0b3
MK
2624 new_name = qemu_mallocz(dirfidp->path.size + vs->name.size + 2);
2625
2626 strcpy(new_name, dirfidp->path.data);
2627 strcat(new_name, "/");
2628 strcat(new_name + dirfidp->path.size, vs->name.data);
2629 } else {
8cf89e00
AL
2630 old_name = vs->fidp->path.data;
2631 end = strrchr(old_name, '/');
2632 if (end) {
2633 end++;
2634 } else {
2635 end = old_name;
2636 }
c7b4b0b3 2637 new_name = qemu_mallocz(end - old_name + vs->name.size + 1);
8cf89e00 2638
c7b4b0b3
MK
2639 strncat(new_name, old_name, end - old_name);
2640 strncat(new_name + (end - old_name), vs->name.data, vs->name.size);
2641 }
8cf89e00 2642
c7b4b0b3
MK
2643 v9fs_string_free(&vs->name);
2644 vs->name.data = qemu_strdup(new_name);
2645 vs->name.size = strlen(new_name);
8cf89e00 2646
c7b4b0b3
MK
2647 if (strcmp(new_name, vs->fidp->path.data) != 0) {
2648 if (v9fs_do_rename(s, &vs->fidp->path, &vs->name)) {
2649 err = -errno;
2650 } else {
2651 V9fsFidState *fidp;
2652 /*
2653 * Fixup fid's pointing to the old name to
2654 * start pointing to the new name
2655 */
2656 for (fidp = s->fid_list; fidp; fidp = fidp->next) {
2657 if (vs->fidp == fidp) {
2658 /*
2659 * we replace name of this fid towards the end
2660 * so that our below strcmp will work
2661 */
2662 continue;
2663 }
2664 if (!strncmp(vs->fidp->path.data, fidp->path.data,
2665 strlen(vs->fidp->path.data))) {
2666 /* replace the name */
2667 v9fs_fix_path(&fidp->path, &vs->name,
2668 strlen(vs->fidp->path.data));
8cf89e00 2669 }
8cf89e00 2670 }
c7b4b0b3 2671 v9fs_string_copy(&vs->fidp->path, &vs->name);
8cf89e00
AL
2672 }
2673 }
c7b4b0b3
MK
2674out:
2675 v9fs_string_free(&vs->name);
2676 return err;
2677}
2678
2679static void v9fs_rename_post_rename(V9fsState *s, V9fsRenameState *vs, int err)
2680{
2681 complete_pdu(s, vs->pdu, err);
2682 qemu_free(vs);
2683}
2684
2685static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
2686{
2687 if (err < 0) {
2688 goto out;
2689 }
2690
2691 if (vs->v9stat.name.size != 0) {
2692 V9fsRenameState *vr;
2693
2694 vr = qemu_malloc(sizeof(V9fsRenameState));
2695 memset(vr, sizeof(*vr), 0);
2696 vr->newdirfid = -1;
2697 vr->pdu = vs->pdu;
2698 vr->fidp = vs->fidp;
2699 vr->offset = vs->offset;
2700 vr->name.size = vs->v9stat.name.size;
2701 vr->name.data = qemu_strdup(vs->v9stat.name.data);
2702
2703 err = v9fs_complete_rename(s, vr);
2704 qemu_free(vr);
2705 }
8cf89e00
AL
2706 v9fs_wstat_post_rename(s, vs, err);
2707 return;
2708
2709out:
2710 v9fs_stat_free(&vs->v9stat);
2711 complete_pdu(s, vs->pdu, err);
2712 qemu_free(vs);
2713}
2714
c7b4b0b3
MK
2715static void v9fs_rename(V9fsState *s, V9fsPDU *pdu)
2716{
2717 int32_t fid;
2718 V9fsRenameState *vs;
2719 ssize_t err = 0;
2720
2721 vs = qemu_malloc(sizeof(*vs));
2722 vs->pdu = pdu;
2723 vs->offset = 7;
2724
2725 pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &vs->newdirfid, &vs->name);
2726
2727 vs->fidp = lookup_fid(s, fid);
2728 if (vs->fidp == NULL) {
2729 err = -ENOENT;
2730 goto out;
2731 }
2732
d62dbb51 2733 BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
c7b4b0b3
MK
2734
2735 err = v9fs_complete_rename(s, vs);
2736 v9fs_rename_post_rename(s, vs, err);
2737 return;
2738out:
2739 complete_pdu(s, vs->pdu, err);
2740 qemu_free(vs);
2741}
2742
8cf89e00
AL
2743static void v9fs_wstat_post_utime(V9fsState *s, V9fsWstatState *vs, int err)
2744{
2745 if (err < 0) {
2746 goto out;
2747 }
2748
f7613bee 2749 if (vs->v9stat.n_gid != -1 || vs->v9stat.n_uid != -1) {
8cf89e00
AL
2750 if (v9fs_do_chown(s, &vs->fidp->path, vs->v9stat.n_uid,
2751 vs->v9stat.n_gid)) {
2752 err = -errno;
2753 }
2754 }
2755 v9fs_wstat_post_chown(s, vs, err);
2756 return;
2757
2758out:
2759 v9fs_stat_free(&vs->v9stat);
2760 complete_pdu(s, vs->pdu, err);
2761 qemu_free(vs);
2762}
2763
2764static void v9fs_wstat_post_chmod(V9fsState *s, V9fsWstatState *vs, int err)
2765{
2766 if (err < 0) {
2767 goto out;
2768 }
2769
74bc02b2 2770 if (vs->v9stat.mtime != -1 || vs->v9stat.atime != -1) {
8fc39ae4
SK
2771 struct timespec times[2];
2772 if (vs->v9stat.atime != -1) {
2773 times[0].tv_sec = vs->v9stat.atime;
2774 times[0].tv_nsec = 0;
2775 } else {
2776 times[0].tv_nsec = UTIME_OMIT;
2777 }
2778 if (vs->v9stat.mtime != -1) {
2779 times[1].tv_sec = vs->v9stat.mtime;
2780 times[1].tv_nsec = 0;
2781 } else {
2782 times[1].tv_nsec = UTIME_OMIT;
2783 }
2784
2785 if (v9fs_do_utimensat(s, &vs->fidp->path, times)) {
8cf89e00
AL
2786 err = -errno;
2787 }
2788 }
2789
2790 v9fs_wstat_post_utime(s, vs, err);
2791 return;
2792
2793out:
2794 v9fs_stat_free(&vs->v9stat);
2795 complete_pdu(s, vs->pdu, err);
2796 qemu_free(vs);
2797}
2798
2799static void v9fs_wstat_post_fsync(V9fsState *s, V9fsWstatState *vs, int err)
2800{
2801 if (err == -1) {
2802 err = -errno;
2803 }
2804 v9fs_stat_free(&vs->v9stat);
2805 complete_pdu(s, vs->pdu, err);
2806 qemu_free(vs);
2807}
2808
2809static void v9fs_wstat_post_lstat(V9fsState *s, V9fsWstatState *vs, int err)
2810{
2811 uint32_t v9_mode;
2812
2813 if (err == -1) {
2814 err = -errno;
2815 goto out;
2816 }
2817
2818 v9_mode = stat_to_v9mode(&vs->stbuf);
2819
2820 if ((vs->v9stat.mode & P9_STAT_MODE_TYPE_BITS) !=
2821 (v9_mode & P9_STAT_MODE_TYPE_BITS)) {
2822 /* Attempting to change the type */
2823 err = -EIO;
2824 goto out;
2825 }
2826
2827 if (v9fs_do_chmod(s, &vs->fidp->path, v9mode_to_mode(vs->v9stat.mode,
2828 &vs->v9stat.extension))) {
2829 err = -errno;
2830 }
2831 v9fs_wstat_post_chmod(s, vs, err);
2832 return;
2833
2834out:
2835 v9fs_stat_free(&vs->v9stat);
2836 complete_pdu(s, vs->pdu, err);
2837 qemu_free(vs);
2838}
2839
9f107513
AL
2840static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
2841{
8cf89e00
AL
2842 int32_t fid;
2843 V9fsWstatState *vs;
2844 int err = 0;
2845
2846 vs = qemu_malloc(sizeof(*vs));
2847 vs->pdu = pdu;
2848 vs->offset = 7;
2849
2850 pdu_unmarshal(pdu, vs->offset, "dwS", &fid, &vs->unused, &vs->v9stat);
2851
2852 vs->fidp = lookup_fid(s, fid);
2853 if (vs->fidp == NULL) {
2854 err = -EINVAL;
2855 goto out;
9f107513 2856 }
8cf89e00
AL
2857
2858 /* do we need to sync the file? */
2859 if (donttouch_stat(&vs->v9stat)) {
d62dbb51 2860 err = v9fs_do_fsync(s, vs->fidp->fs.fd);
8cf89e00
AL
2861 v9fs_wstat_post_fsync(s, vs, err);
2862 return;
2863 }
2864
2865 if (vs->v9stat.mode != -1) {
2866 err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
2867 v9fs_wstat_post_lstat(s, vs, err);
2868 return;
2869 }
2870
2871 v9fs_wstat_post_chmod(s, vs, err);
2872 return;
2873
2874out:
2875 v9fs_stat_free(&vs->v9stat);
2876 complete_pdu(s, vs->pdu, err);
2877 qemu_free(vs);
9f107513
AL
2878}
2879
be940c87
MK
2880static void v9fs_statfs_post_statfs(V9fsState *s, V9fsStatfsState *vs, int err)
2881{
5e94c103
MK
2882 int32_t bsize_factor;
2883
be940c87
MK
2884 if (err) {
2885 err = -errno;
2886 goto out;
2887 }
2888
5e94c103
MK
2889 /*
2890 * compute bsize factor based on host file system block size
2891 * and client msize
2892 */
2893 bsize_factor = (s->msize - P9_IOHDRSZ)/vs->stbuf.f_bsize;
2894 if (!bsize_factor) {
2895 bsize_factor = 1;
2896 }
be940c87
MK
2897 vs->v9statfs.f_type = vs->stbuf.f_type;
2898 vs->v9statfs.f_bsize = vs->stbuf.f_bsize;
5e94c103
MK
2899 vs->v9statfs.f_bsize *= bsize_factor;
2900 /*
2901 * f_bsize is adjusted(multiplied) by bsize factor, so we need to
2902 * adjust(divide) the number of blocks, free blocks and available
2903 * blocks by bsize factor
2904 */
2905 vs->v9statfs.f_blocks = vs->stbuf.f_blocks/bsize_factor;
2906 vs->v9statfs.f_bfree = vs->stbuf.f_bfree/bsize_factor;
2907 vs->v9statfs.f_bavail = vs->stbuf.f_bavail/bsize_factor;
be940c87
MK
2908 vs->v9statfs.f_files = vs->stbuf.f_files;
2909 vs->v9statfs.f_ffree = vs->stbuf.f_ffree;
2910 vs->v9statfs.fsid_val = (unsigned int) vs->stbuf.f_fsid.__val[0] |
2911 (unsigned long long)vs->stbuf.f_fsid.__val[1] << 32;
2912 vs->v9statfs.f_namelen = vs->stbuf.f_namelen;
2913
2914 vs->offset += pdu_marshal(vs->pdu, vs->offset, "ddqqqqqqd",
2915 vs->v9statfs.f_type, vs->v9statfs.f_bsize, vs->v9statfs.f_blocks,
2916 vs->v9statfs.f_bfree, vs->v9statfs.f_bavail, vs->v9statfs.f_files,
2917 vs->v9statfs.f_ffree, vs->v9statfs.fsid_val,
2918 vs->v9statfs.f_namelen);
2919
2920out:
2921 complete_pdu(s, vs->pdu, vs->offset);
2922 qemu_free(vs);
2923}
2924
2925static void v9fs_statfs(V9fsState *s, V9fsPDU *pdu)
2926{
2927 V9fsStatfsState *vs;
2928 ssize_t err = 0;
2929
2930 vs = qemu_malloc(sizeof(*vs));
2931 vs->pdu = pdu;
2932 vs->offset = 7;
2933
2934 memset(&vs->v9statfs, 0, sizeof(vs->v9statfs));
2935
2936 pdu_unmarshal(vs->pdu, vs->offset, "d", &vs->fid);
2937
2938 vs->fidp = lookup_fid(s, vs->fid);
2939 if (vs->fidp == NULL) {
2940 err = -ENOENT;
2941 goto out;
2942 }
2943
2944 err = v9fs_do_statfs(s, &vs->fidp->path, &vs->stbuf);
2945 v9fs_statfs_post_statfs(s, vs, err);
2946 return;
2947
2948out:
2949 complete_pdu(s, vs->pdu, err);
2950 qemu_free(vs);
2951}
2952
5268cecc
MK
2953static void v9fs_mknod_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
2954{
2955 if (err == -1) {
2956 err = -errno;
2957 goto out;
2958 }
2959
2960 stat_to_qid(&vs->stbuf, &vs->qid);
2961 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
2962 err = vs->offset;
2963out:
2964 complete_pdu(s, vs->pdu, err);
2965 v9fs_string_free(&vs->fullname);
2966 v9fs_string_free(&vs->name);
2967 qemu_free(vs);
2968}
2969
2970static void v9fs_mknod_post_mknod(V9fsState *s, V9fsMkState *vs, int err)
2971{
2972 if (err == -1) {
2973 err = -errno;
2974 goto out;
2975 }
2976
2977 err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
2978 v9fs_mknod_post_lstat(s, vs, err);
2979 return;
2980out:
2981 complete_pdu(s, vs->pdu, err);
2982 v9fs_string_free(&vs->fullname);
2983 v9fs_string_free(&vs->name);
2984 qemu_free(vs);
2985}
2986
2987static void v9fs_mknod(V9fsState *s, V9fsPDU *pdu)
2988{
2989 int32_t fid;
2990 V9fsMkState *vs;
2991 int err = 0;
2992 V9fsFidState *fidp;
2993 gid_t gid;
2994 int mode;
2995 int major, minor;
2996
2997 vs = qemu_malloc(sizeof(*vs));
2998 vs->pdu = pdu;
2999 vs->offset = 7;
3000
3001 v9fs_string_init(&vs->fullname);
3002 pdu_unmarshal(vs->pdu, vs->offset, "dsdddd", &fid, &vs->name, &mode,
3003 &major, &minor, &gid);
3004
3005 fidp = lookup_fid(s, fid);
3006 if (fidp == NULL) {
3007 err = -ENOENT;
3008 goto out;
3009 }
3010
3011 v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
3012 err = v9fs_do_mknod(s, vs->fullname.data, mode, makedev(major, minor),
3013 fidp->uid, gid);
3014 v9fs_mknod_post_mknod(s, vs, err);
3015 return;
3016
3017out:
3018 complete_pdu(s, vs->pdu, err);
3019 v9fs_string_free(&vs->fullname);
3020 v9fs_string_free(&vs->name);
3021 qemu_free(vs);
3022}
3023
b67592ea
MK
3024static void v9fs_mkdir_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
3025{
3026 if (err == -1) {
3027 err = -errno;
3028 goto out;
3029 }
3030
3031 stat_to_qid(&vs->stbuf, &vs->qid);
3032 vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
3033 err = vs->offset;
3034out:
3035 complete_pdu(s, vs->pdu, err);
3036 v9fs_string_free(&vs->fullname);
3037 v9fs_string_free(&vs->name);
3038 qemu_free(vs);
3039}
3040
3041static void v9fs_mkdir_post_mkdir(V9fsState *s, V9fsMkState *vs, int err)
3042{
3043 if (err == -1) {
3044 err = -errno;
3045 goto out;
3046 }
3047
3048 err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
3049 v9fs_mkdir_post_lstat(s, vs, err);
3050 return;
3051out:
3052 complete_pdu(s, vs->pdu, err);
3053 v9fs_string_free(&vs->fullname);
3054 v9fs_string_free(&vs->name);
3055 qemu_free(vs);
3056}
3057
3058static void v9fs_mkdir(V9fsState *s, V9fsPDU *pdu)
3059{
3060 int32_t fid;
3061 V9fsMkState *vs;
3062 int err = 0;
3063 V9fsFidState *fidp;
3064 gid_t gid;
3065 int mode;
3066
3067 vs = qemu_malloc(sizeof(*vs));
3068 vs->pdu = pdu;
3069 vs->offset = 7;
3070
3071 v9fs_string_init(&vs->fullname);
3072 pdu_unmarshal(vs->pdu, vs->offset, "dsdd", &fid, &vs->name, &mode,
3073 &gid);
3074
3075 fidp = lookup_fid(s, fid);
3076 if (fidp == NULL) {
3077 err = -ENOENT;
3078 goto out;
3079 }
3080
3081 v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
3082 err = v9fs_do_mkdir(s, vs->fullname.data, mode, fidp->uid, gid);
3083 v9fs_mkdir_post_mkdir(s, vs, err);
3084 return;
3085
3086out:
3087 complete_pdu(s, vs->pdu, err);
3088 v9fs_string_free(&vs->fullname);
3089 v9fs_string_free(&vs->name);
3090 qemu_free(vs);
3091}
3092
9f107513
AL
3093typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
3094
3095static pdu_handler_t *pdu_handlers[] = {
c18e2f94 3096 [P9_TREADDIR] = v9fs_readdir,
be940c87 3097 [P9_TSTATFS] = v9fs_statfs,
00ede4c2 3098 [P9_TGETATTR] = v9fs_getattr,
c79ce737 3099 [P9_TSETATTR] = v9fs_setattr,
5268cecc 3100 [P9_TMKNOD] = v9fs_mknod,
c7b4b0b3 3101 [P9_TRENAME] = v9fs_rename,
b67592ea 3102 [P9_TMKDIR] = v9fs_mkdir,
9f107513 3103 [P9_TVERSION] = v9fs_version,
771e9d4c 3104 [P9_TLOPEN] = v9fs_open,
9f107513
AL
3105 [P9_TATTACH] = v9fs_attach,
3106 [P9_TSTAT] = v9fs_stat,
3107 [P9_TWALK] = v9fs_walk,
3108 [P9_TCLUNK] = v9fs_clunk,
3109 [P9_TOPEN] = v9fs_open,
3110 [P9_TREAD] = v9fs_read,
3111#if 0
3112 [P9_TAUTH] = v9fs_auth,
3113#endif
3114 [P9_TFLUSH] = v9fs_flush,
b2c224be 3115 [P9_TLINK] = v9fs_link,
08c60fc9 3116 [P9_TSYMLINK] = v9fs_symlink,
9f107513 3117 [P9_TCREATE] = v9fs_create,
c1568af5 3118 [P9_TLCREATE] = v9fs_lcreate,
9f107513
AL
3119 [P9_TWRITE] = v9fs_write,
3120 [P9_TWSTAT] = v9fs_wstat,
3121 [P9_TREMOVE] = v9fs_remove,
3122};
3123
3124static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
3125{
3126 pdu_handler_t *handler;
3127
3128 if (debug_9p_pdu) {
3129 pprint_pdu(pdu);
3130 }
3131
3132 BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers));
3133
3134 handler = pdu_handlers[pdu->id];
3135 BUG_ON(handler == NULL);
3136
3137 handler(s, pdu);
3138}
3139
3140static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
3141{
3142 V9fsState *s = (V9fsState *)vdev;
3143 V9fsPDU *pdu;
3144 ssize_t len;
3145
3146 while ((pdu = alloc_pdu(s)) &&
3147 (len = virtqueue_pop(vq, &pdu->elem)) != 0) {
3148 uint8_t *ptr;
3149
3150 BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
3151 BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
3152
3153 ptr = pdu->elem.out_sg[0].iov_base;
3154
3155 memcpy(&pdu->size, ptr, 4);
3156 pdu->id = ptr[4];
3157 memcpy(&pdu->tag, ptr + 5, 2);
3158
3159 submit_pdu(s, pdu);
3160 }
3161
3162 free_pdu(s, pdu);
3163}
3164
3165static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
3166{
3167 features |= 1 << VIRTIO_9P_MOUNT_TAG;
3168 return features;
3169}
3170
3171static V9fsState *to_virtio_9p(VirtIODevice *vdev)
3172{
3173 return (V9fsState *)vdev;
3174}
3175
3176static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
3177{
3178 struct virtio_9p_config *cfg;
3179 V9fsState *s = to_virtio_9p(vdev);
3180
3181 cfg = qemu_mallocz(sizeof(struct virtio_9p_config) +
3182 s->tag_len);
3183 stw_raw(&cfg->tag_len, s->tag_len);
3184 memcpy(cfg->tag, s->tag, s->tag_len);
3185 memcpy(config, cfg, s->config_size);
3186 qemu_free(cfg);
3187}
3188
3189VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
3190 {
3191 V9fsState *s;
3192 int i, len;
3193 struct stat stat;
3194 FsTypeEntry *fse;
3195
3196
3197 s = (V9fsState *)virtio_common_init("virtio-9p",
3198 VIRTIO_ID_9P,
3199 sizeof(struct virtio_9p_config)+
3200 MAX_TAG_LEN,
3201 sizeof(V9fsState));
3202
3203 /* initialize pdu allocator */
3204 QLIST_INIT(&s->free_list);
3205 for (i = 0; i < (MAX_REQ - 1); i++) {
3206 QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
3207 }
3208
3209 s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output);
3210
3211 fse = get_fsdev_fsentry(conf->fsdev_id);
3212
3213 if (!fse) {
3214 /* We don't have a fsdev identified by fsdev_id */
3215 fprintf(stderr, "Virtio-9p device couldn't find fsdev "
3216 "with the id %s\n", conf->fsdev_id);
3217 exit(1);
3218 }
3219
3220 if (!fse->path || !conf->tag) {
3221 /* we haven't specified a mount_tag or the path */
3222 fprintf(stderr, "fsdev with id %s needs path "
3223 "and Virtio-9p device needs mount_tag arguments\n",
3224 conf->fsdev_id);
3225 exit(1);
3226 }
3227
758e8e38
VJ
3228 if (!strcmp(fse->security_model, "passthrough")) {
3229 /* Files on the Fileserver set to client user credentials */
3230 s->ctx.fs_sm = SM_PASSTHROUGH;
3231 } else if (!strcmp(fse->security_model, "mapped")) {
3232 /* Files on the fileserver are set to QEMU credentials.
3233 * Client user credentials are saved in extended attributes.
3234 */
3235 s->ctx.fs_sm = SM_MAPPED;
3236 } else {
9ce56db6
VJ
3237 /* user haven't specified a correct security option */
3238 fprintf(stderr, "one of the following must be specified as the"
3239 "security option:\n\t security_model=passthrough \n\t "
3240 "security_model=mapped\n");
3241 return NULL;
3242 }
3243
9f107513
AL
3244 if (lstat(fse->path, &stat)) {
3245 fprintf(stderr, "share path %s does not exist\n", fse->path);
3246 exit(1);
3247 } else if (!S_ISDIR(stat.st_mode)) {
3248 fprintf(stderr, "share path %s is not a directory \n", fse->path);
3249 exit(1);
3250 }
3251
3252 s->ctx.fs_root = qemu_strdup(fse->path);
3253 len = strlen(conf->tag);
3254 if (len > MAX_TAG_LEN) {
3255 len = MAX_TAG_LEN;
3256 }
3257 /* s->tag is non-NULL terminated string */
3258 s->tag = qemu_malloc(len);
3259 memcpy(s->tag, conf->tag, len);
3260 s->tag_len = len;
3261 s->ctx.uid = -1;
3262
3263 s->ops = fse->ops;
3264 s->vdev.get_features = virtio_9p_get_features;
3265 s->config_size = sizeof(struct virtio_9p_config) +
3266 s->tag_len;
3267 s->vdev.get_config = virtio_9p_get_config;
3268
3269 return &s->vdev;
3270}