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

P
Paolo Bonzini 已提交
14 15
#include "hw/virtio/virtio.h"
#include "hw/i386/pc.h"
16
#include "qemu/error-report.h"
M
Michael S. Tsirkin 已提交
17
#include "qemu/iov.h"
18
#include "qemu/sockets.h"
19 20
#include "virtio-9p.h"
#include "fsdev/qemu-fsdev.h"
21
#include "9p-xattr.h"
22
#include "coth.h"
23
#include "trace.h"
24
#include "migration/migration.h"
25

26 27 28
int open_fd_hw;
int total_open_fd;
static int open_fd_rc;
29

30 31 32 33 34 35 36 37 38 39 40 41
enum {
    Oread   = 0x00,
    Owrite  = 0x01,
    Ordwr   = 0x02,
    Oexec   = 0x03,
    Oexcl   = 0x04,
    Otrunc  = 0x10,
    Orexec  = 0x20,
    Orclose = 0x40,
    Oappend = 0x80,
};

42 43 44 45 46 47
ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
{
    ssize_t ret;
    va_list ap;

    va_start(ap, fmt);
48
    ret = virtio_pdu_vmarshal(pdu, offset, fmt, ap);
49 50 51 52 53 54 55 56 57 58 59
    va_end(ap);

    return ret;
}

ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
{
    ssize_t ret;
    va_list ap;

    va_start(ap, fmt);
60
    ret = virtio_pdu_vunmarshal(pdu, offset, fmt, ap);
61 62 63 64 65
    va_end(ap);

    return ret;
}

W
Wei Liu 已提交
66 67
static void pdu_push_and_notify(V9fsPDU *pdu)
{
68
    virtio_9p_push_and_notify(pdu);
W
Wei Liu 已提交
69 70
}

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
static int omode_to_uflags(int8_t mode)
{
    int ret = 0;

    switch (mode & 3) {
    case Oread:
        ret = O_RDONLY;
        break;
    case Ordwr:
        ret = O_RDWR;
        break;
    case Owrite:
        ret = O_WRONLY;
        break;
    case Oexec:
        ret = O_RDONLY;
        break;
    }

    if (mode & Otrunc) {
        ret |= O_TRUNC;
    }

    if (mode & Oappend) {
        ret |= O_APPEND;
    }

    if (mode & Oexcl) {
        ret |= O_EXCL;
    }

    return ret;
}

M
M. Mohan Kumar 已提交
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
struct dotl_openflag_map {
    int dotl_flag;
    int open_flag;
};

static int dotl_to_open_flags(int flags)
{
    int i;
    /*
     * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY
     * and P9_DOTL_NOACCESS
     */
    int oflags = flags & O_ACCMODE;

    struct dotl_openflag_map dotl_oflag_map[] = {
        { P9_DOTL_CREATE, O_CREAT },
        { P9_DOTL_EXCL, O_EXCL },
        { P9_DOTL_NOCTTY , O_NOCTTY },
        { P9_DOTL_TRUNC, O_TRUNC },
        { P9_DOTL_APPEND, O_APPEND },
        { P9_DOTL_NONBLOCK, O_NONBLOCK } ,
        { P9_DOTL_DSYNC, O_DSYNC },
        { P9_DOTL_FASYNC, FASYNC },
        { P9_DOTL_DIRECT, O_DIRECT },
        { P9_DOTL_LARGEFILE, O_LARGEFILE },
        { P9_DOTL_DIRECTORY, O_DIRECTORY },
        { P9_DOTL_NOFOLLOW, O_NOFOLLOW },
        { P9_DOTL_NOATIME, O_NOATIME },
        { P9_DOTL_SYNC, O_SYNC },
    };

    for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) {
        if (flags & dotl_oflag_map[i].dotl_flag) {
            oflags |= dotl_oflag_map[i].open_flag;
        }
    }

    return oflags;
}

145
void cred_init(FsCred *credp)
146
{
147 148 149 150
    credp->fc_uid = -1;
    credp->fc_gid = -1;
    credp->fc_mode = -1;
    credp->fc_rdev = -1;
151 152
}

153 154 155 156 157 158
static int get_dotl_openflags(V9fsState *s, int oflags)
{
    int flags;
    /*
     * Filter the client open flags
     */
M
M. Mohan Kumar 已提交
159 160
    flags = dotl_to_open_flags(oflags);
    flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
161 162 163 164 165 166 167
    /*
     * Ignore direct disk access hint until the server supports it.
     */
    flags &= ~O_DIRECT;
    return flags;
}

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
void v9fs_path_init(V9fsPath *path)
{
    path->data = NULL;
    path->size = 0;
}

void v9fs_path_free(V9fsPath *path)
{
    g_free(path->data);
    path->data = NULL;
    path->size = 0;
}

void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs)
{
    v9fs_path_free(lhs);
    lhs->data = g_malloc(rhs->size);
    memcpy(lhs->data, rhs->data, rhs->size);
    lhs->size = rhs->size;
}

int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
                      const char *name, V9fsPath *path)
{
    int err;
    err = s->ops->name_to_path(&s->ctx, dirpath, name, path);
    if (err < 0) {
        err = -errno;
    }
    return err;
}

200 201 202 203 204 205
/*
 * Return TRUE if s1 is an ancestor of s2.
 *
 * E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d".
 * As a special case, We treat s1 as ancestor of s2 if they are same!
 */
206
static int v9fs_path_is_ancestor(V9fsPath *s1, V9fsPath *s2)
207
{
208 209
    if (!strncmp(s1->data, s2->data, s1->size - 1)) {
        if (s2->data[s1->size - 1] == '\0' || s2->data[s1->size - 1] == '/') {
210 211 212 213 214 215
            return 1;
        }
    }
    return 0;
}

216 217 218 219 220
static size_t v9fs_string_size(V9fsString *str)
{
    return str->size;
}

221 222
/*
 * returns 0 if fid got re-opened, 1 if not, < 0 on error */
223
static int v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f)
224 225 226 227 228
{
    int err = 1;
    if (f->fid_type == P9_FID_FILE) {
        if (f->fs.fd == -1) {
            do {
229 230
                err = v9fs_co_open(pdu, f, f->open_flags);
            } while (err == -EINTR && !pdu->cancelled);
231 232 233 234
        }
    } else if (f->fid_type == P9_FID_DIR) {
        if (f->fs.dir == NULL) {
            do {
235 236
                err = v9fs_co_opendir(pdu, f);
            } while (err == -EINTR && !pdu->cancelled);
237 238 239 240 241
        }
    }
    return err;
}

242
static V9fsFidState *get_fid(V9fsPDU *pdu, int32_t fid)
243
{
244
    int err;
245
    V9fsFidState *f;
246
    V9fsState *s = pdu->s;
247 248

    for (f = s->fid_list; f; f = f->next) {
249
        BUG_ON(f->clunked);
250
        if (f->fid == fid) {
251 252 253 254 255
            /*
             * Update the fid ref upfront so that
             * we don't get reclaimed when we yield
             * in open later.
             */
256
            f->ref++;
257 258 259 260 261 262
            /*
             * check whether we need to reopen the
             * file. We might have closed the fd
             * while trying to free up some file
             * descriptors.
             */
263
            err = v9fs_reopen_fid(pdu, f);
264 265 266 267
            if (err < 0) {
                f->ref--;
                return NULL;
            }
268 269 270 271 272
            /*
             * Mark the fid as referenced so that the LRU
             * reclaim won't close the file descriptor
             */
            f->flags |= FID_REFERENCED;
273 274 275 276 277 278 279 280 281 282
            return f;
        }
    }
    return NULL;
}

static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
{
    V9fsFidState *f;

283 284 285 286 287 288
    for (f = s->fid_list; f; f = f->next) {
        /* If fid is already there return NULL */
        BUG_ON(f->clunked);
        if (f->fid == fid) {
            return NULL;
        }
289
    }
290
    f = g_malloc0(sizeof(V9fsFidState));
291
    f->fid = fid;
292
    f->fid_type = P9_FID_NONE;
293
    f->ref = 1;
294 295 296 297 298
    /*
     * Mark the fid as referenced so that the LRU
     * reclaim won't close the file descriptor
     */
    f->flags |= FID_REFERENCED;
299 300 301 302 303 304
    f->next = s->fid_list;
    s->fid_list = f;

    return f;
}

305
static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp)
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
{
    int retval = 0;

    if (fidp->fs.xattr.copied_len == -1) {
        /* getxattr/listxattr fid */
        goto free_value;
    }
    /*
     * if this is fid for setxattr. clunk should
     * result in setxattr localcall
     */
    if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) {
        /* clunk after partial write */
        retval = -EINVAL;
        goto free_out;
    }
322
    if (fidp->fs.xattr.len) {
323
        retval = v9fs_co_lsetxattr(pdu, &fidp->path, &fidp->fs.xattr.name,
324 325 326 327
                                   fidp->fs.xattr.value,
                                   fidp->fs.xattr.len,
                                   fidp->fs.xattr.flags);
    } else {
328
        retval = v9fs_co_lremovexattr(pdu, &fidp->path, &fidp->fs.xattr.name);
329
    }
330 331 332
free_out:
    v9fs_string_free(&fidp->fs.xattr.name);
free_value:
333
    g_free(fidp->fs.xattr.value);
334 335 336
    return retval;
}

337
static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp)
338
{
339
    int retval = 0;
340 341

    if (fidp->fid_type == P9_FID_FILE) {
342 343
        /* If we reclaimed the fd no need to close */
        if (fidp->fs.fd != -1) {
344
            retval = v9fs_co_close(pdu, &fidp->fs);
345
        }
346
    } else if (fidp->fid_type == P9_FID_DIR) {
347
        if (fidp->fs.dir != NULL) {
348
            retval = v9fs_co_closedir(pdu, &fidp->fs);
349
        }
350
    } else if (fidp->fid_type == P9_FID_XATTR) {
351
        retval = v9fs_xattr_fid_clunk(pdu, fidp);
352
    }
353
    v9fs_path_free(&fidp->path);
354 355 356 357
    g_free(fidp);
    return retval;
}

358
static int put_fid(V9fsPDU *pdu, V9fsFidState *fidp)
359 360 361
{
    BUG_ON(!fidp->ref);
    fidp->ref--;
362 363 364
    /*
     * Don't free the fid if it is in reclaim list
     */
365
    if (!fidp->ref && fidp->clunked) {
366 367 368 369 370 371 372 373 374 375 376 377 378
        if (fidp->fid == pdu->s->root_fid) {
            /*
             * if the clunked fid is root fid then we
             * have unmounted the fs on the client side.
             * delete the migration blocker. Ideally, this
             * should be hooked to transport close notification
             */
            if (pdu->s->migration_blocker) {
                migrate_del_blocker(pdu->s->migration_blocker);
                error_free(pdu->s->migration_blocker);
                pdu->s->migration_blocker = NULL;
            }
        }
379
        return free_fid(pdu, fidp);
380
    }
381
    return 0;
382 383
}

384
static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid)
385
{
386 387 388 389 390 391 392 393
    V9fsFidState **fidpp, *fidp;

    for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
        if ((*fidpp)->fid == fid) {
            break;
        }
    }
    if (*fidpp == NULL) {
394
        return NULL;
395 396 397
    }
    fidp = *fidpp;
    *fidpp = fidp->next;
398
    fidp->clunked = 1;
399
    return fidp;
400 401
}

402
void v9fs_reclaim_fd(V9fsPDU *pdu)
403 404
{
    int reclaim_count = 0;
405
    V9fsState *s = pdu->s;
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
    V9fsFidState *f, *reclaim_list = NULL;

    for (f = s->fid_list; f; f = f->next) {
        /*
         * Unlink fids cannot be reclaimed. Check
         * for them and skip them. Also skip fids
         * currently being operated on.
         */
        if (f->ref || f->flags & FID_NON_RECLAIMABLE) {
            continue;
        }
        /*
         * if it is a recently referenced fid
         * we leave the fid untouched and clear the
         * reference bit. We come back to it later
         * in the next iteration. (a simple LRU without
         * moving list elements around)
         */
        if (f->flags & FID_REFERENCED) {
            f->flags &= ~FID_REFERENCED;
            continue;
        }
        /*
         * Add fids to reclaim list.
         */
        if (f->fid_type == P9_FID_FILE) {
            if (f->fs.fd != -1) {
                /*
                 * Up the reference count so that
                 * a clunk request won't free this fid
                 */
                f->ref++;
                f->rclm_lst = reclaim_list;
                reclaim_list = f;
                f->fs_reclaim.fd = f->fs.fd;
                f->fs.fd = -1;
                reclaim_count++;
            }
444 445 446 447 448 449 450 451 452 453 454 455 456
        } else if (f->fid_type == P9_FID_DIR) {
            if (f->fs.dir != NULL) {
                /*
                 * Up the reference count so that
                 * a clunk request won't free this fid
                 */
                f->ref++;
                f->rclm_lst = reclaim_list;
                reclaim_list = f;
                f->fs_reclaim.dir = f->fs.dir;
                f->fs.dir = NULL;
                reclaim_count++;
            }
457 458 459 460 461 462 463 464 465 466 467 468 469
        }
        if (reclaim_count >= open_fd_rc) {
            break;
        }
    }
    /*
     * Now close the fid in reclaim list. Free them if they
     * are already clunked.
     */
    while (reclaim_list) {
        f = reclaim_list;
        reclaim_list = f->rclm_lst;
        if (f->fid_type == P9_FID_FILE) {
470
            v9fs_co_close(pdu, &f->fs_reclaim);
471
        } else if (f->fid_type == P9_FID_DIR) {
472
            v9fs_co_closedir(pdu, &f->fs_reclaim);
473 474 475 476 477 478
        }
        f->rclm_lst = NULL;
        /*
         * Now drop the fid reference, free it
         * if clunked.
         */
479
        put_fid(pdu, f);
480 481 482
    }
}

483
static int v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path)
484 485
{
    int err;
486
    V9fsState *s = pdu->s;
487 488 489 490
    V9fsFidState *fidp, head_fid;

    head_fid.next = s->fid_list;
    for (fidp = s->fid_list; fidp; fidp = fidp->next) {
491 492 493 494
        if (fidp->path.size != path->size) {
            continue;
        }
        if (!memcmp(fidp->path.data, path->data, path->size)) {
495 496
            /* Mark the fid non reclaimable. */
            fidp->flags |= FID_NON_RECLAIMABLE;
497 498

            /* reopen the file/dir if already closed */
499
            err = v9fs_reopen_fid(pdu, fidp);
500 501 502 503 504 505 506 507 508
            if (err < 0) {
                return -1;
            }
            /*
             * Go back to head of fid list because
             * the list could have got updated when
             * switched to the worker thread
             */
            if (err == 0) {
509 510 511 512 513 514 515
                fidp = &head_fid;
            }
        }
    }
    return 0;
}

516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
static void virtfs_reset(V9fsPDU *pdu)
{
    V9fsState *s = pdu->s;
    V9fsFidState *fidp = NULL;

    /* Free all fids */
    while (s->fid_list) {
        fidp = s->fid_list;
        s->fid_list = fidp->next;

        if (fidp->ref) {
            fidp->clunked = 1;
        } else {
            free_fid(pdu, fidp);
        }
    }
    if (fidp) {
        /* One or more unclunked fids found... */
        error_report("9pfs:%s: One or more uncluncked fids "
                     "found during reset", __func__);
    }
}

539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
#define P9_QID_TYPE_DIR         0x80
#define P9_QID_TYPE_SYMLINK     0x02

#define P9_STAT_MODE_DIR        0x80000000
#define P9_STAT_MODE_APPEND     0x40000000
#define P9_STAT_MODE_EXCL       0x20000000
#define P9_STAT_MODE_MOUNT      0x10000000
#define P9_STAT_MODE_AUTH       0x08000000
#define P9_STAT_MODE_TMP        0x04000000
#define P9_STAT_MODE_SYMLINK    0x02000000
#define P9_STAT_MODE_LINK       0x01000000
#define P9_STAT_MODE_DEVICE     0x00800000
#define P9_STAT_MODE_NAMED_PIPE 0x00200000
#define P9_STAT_MODE_SOCKET     0x00100000
#define P9_STAT_MODE_SETUID     0x00080000
#define P9_STAT_MODE_SETGID     0x00040000
#define P9_STAT_MODE_SETVTX     0x00010000

#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR |          \
                                P9_STAT_MODE_SYMLINK |      \
                                P9_STAT_MODE_LINK |         \
                                P9_STAT_MODE_DEVICE |       \
                                P9_STAT_MODE_NAMED_PIPE |   \
                                P9_STAT_MODE_SOCKET)

/* This is the algorithm from ufs in spfs */
static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
{
    size_t size;

569
    memset(&qidp->path, 0, sizeof(qidp->path));
570 571 572 573 574 575 576 577 578 579 580 581
    size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
    memcpy(&qidp->path, &stbuf->st_ino, size);
    qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
    qidp->type = 0;
    if (S_ISDIR(stbuf->st_mode)) {
        qidp->type |= P9_QID_TYPE_DIR;
    }
    if (S_ISLNK(stbuf->st_mode)) {
        qidp->type |= P9_QID_TYPE_SYMLINK;
    }
}

582
static int fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, V9fsQID *qidp)
583 584 585 586
{
    struct stat stbuf;
    int err;

587
    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
588
    if (err < 0) {
589 590 591 592 593 594
        return err;
    }
    stat_to_qid(&stbuf, qidp);
    return 0;
}

W
Wei Liu 已提交
595
V9fsPDU *pdu_alloc(V9fsState *s)
596 597 598 599
{
    V9fsPDU *pdu = NULL;

    if (!QLIST_EMPTY(&s->free_list)) {
600 601 602
        pdu = QLIST_FIRST(&s->free_list);
        QLIST_REMOVE(pdu, next);
        QLIST_INSERT_HEAD(&s->active_list, pdu, next);
603 604 605 606
    }
    return pdu;
}

W
Wei Liu 已提交
607
void pdu_free(V9fsPDU *pdu)
608 609
{
    if (pdu) {
610
        V9fsState *s = pdu->s;
611 612 613 614 615 616 617 618
        /*
         * Cancelled pdu are added back to the freelist
         * by flush request .
         */
        if (!pdu->cancelled) {
            QLIST_REMOVE(pdu, next);
            QLIST_INSERT_HEAD(&s->free_list, pdu, next);
        }
619 620 621
    }
}

622 623 624 625 626
/*
 * We don't do error checking for pdu_marshal/unmarshal here
 * because we always expect to have enough space to encode
 * error details
 */
627
static void pdu_complete(V9fsPDU *pdu, ssize_t len)
628 629
{
    int8_t id = pdu->id + 1; /* Response */
630
    V9fsState *s = pdu->s;
631 632 633

    if (len < 0) {
        int err = -len;
634
        len = 7;
635

636 637 638 639 640 641 642 643 644
        if (s->proto_version != V9FS_PROTO_2000L) {
            V9fsString str;

            str.data = strerror(err);
            str.size = strlen(str.data);

            len += pdu_marshal(pdu, len, "s", &str);
            id = P9_RERROR;
        }
645

646
        len += pdu_marshal(pdu, len, "d", err);
647

648 649 650
        if (s->proto_version == V9FS_PROTO_2000L) {
            id = P9_RLERROR;
        }
651
        trace_v9fs_rerror(pdu->tag, pdu->id, err); /* Trace ERROR */
652 653 654 655 656 657 658 659 660
    }

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

    /* keep these in sync */
    pdu->size = len;
    pdu->id = id;

W
Wei Liu 已提交
661
    pdu_push_and_notify(pdu);
662

663 664 665
    /* Now wakeup anybody waiting in flush for this request */
    qemu_co_queue_next(&pdu->complete);

666
    pdu_free(pdu);
667 668
}

669 670 671 672 673 674 675 676 677
static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
{
    mode_t ret;

    ret = mode & 0777;
    if (mode & P9_STAT_MODE_DIR) {
        ret |= S_IFDIR;
    }

678 679 680 681 682 683 684 685 686 687
    if (mode & P9_STAT_MODE_SYMLINK) {
        ret |= S_IFLNK;
    }
    if (mode & P9_STAT_MODE_SOCKET) {
        ret |= S_IFSOCK;
    }
    if (mode & P9_STAT_MODE_NAMED_PIPE) {
        ret |= S_IFIFO;
    }
    if (mode & P9_STAT_MODE_DEVICE) {
688
        if (extension->size && extension->data[0] == 'c') {
689 690 691
            ret |= S_IFCHR;
        } else {
            ret |= S_IFBLK;
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
        }
    }

    if (!(ret&~0777)) {
        ret |= S_IFREG;
    }

    if (mode & P9_STAT_MODE_SETUID) {
        ret |= S_ISUID;
    }
    if (mode & P9_STAT_MODE_SETGID) {
        ret |= S_ISGID;
    }
    if (mode & P9_STAT_MODE_SETVTX) {
        ret |= S_ISVTX;
    }

    return ret;
}

static int donttouch_stat(V9fsStat *stat)
{
    if (stat->type == -1 &&
        stat->dev == -1 &&
        stat->qid.type == -1 &&
        stat->qid.version == -1 &&
        stat->qid.path == -1 &&
        stat->mode == -1 &&
        stat->atime == -1 &&
        stat->mtime == -1 &&
        stat->length == -1 &&
        !stat->name.size &&
        !stat->uid.size &&
        !stat->gid.size &&
        !stat->muid.size &&
        stat->n_uid == -1 &&
        stat->n_gid == -1 &&
        stat->n_muid == -1) {
        return 1;
    }

    return 0;
}

736 737 738 739 740 741 742 743 744
static void v9fs_stat_init(V9fsStat *stat)
{
    v9fs_string_init(&stat->name);
    v9fs_string_init(&stat->uid);
    v9fs_string_init(&stat->gid);
    v9fs_string_init(&stat->muid);
    v9fs_string_init(&stat->extension);
}

745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
static void v9fs_stat_free(V9fsStat *stat)
{
    v9fs_string_free(&stat->name);
    v9fs_string_free(&stat->uid);
    v9fs_string_free(&stat->gid);
    v9fs_string_free(&stat->muid);
    v9fs_string_free(&stat->extension);
}

static uint32_t stat_to_v9mode(const struct stat *stbuf)
{
    uint32_t mode;

    mode = stbuf->st_mode & 0777;
    if (S_ISDIR(stbuf->st_mode)) {
        mode |= P9_STAT_MODE_DIR;
    }

763 764 765
    if (S_ISLNK(stbuf->st_mode)) {
        mode |= P9_STAT_MODE_SYMLINK;
    }
766

767 768 769
    if (S_ISSOCK(stbuf->st_mode)) {
        mode |= P9_STAT_MODE_SOCKET;
    }
770

771 772 773
    if (S_ISFIFO(stbuf->st_mode)) {
        mode |= P9_STAT_MODE_NAMED_PIPE;
    }
774

775 776 777
    if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
        mode |= P9_STAT_MODE_DEVICE;
    }
778

779 780 781
    if (stbuf->st_mode & S_ISUID) {
        mode |= P9_STAT_MODE_SETUID;
    }
782

783 784 785
    if (stbuf->st_mode & S_ISGID) {
        mode |= P9_STAT_MODE_SETGID;
    }
786

787 788
    if (stbuf->st_mode & S_ISVTX) {
        mode |= P9_STAT_MODE_SETVTX;
789 790 791 792 793
    }

    return mode;
}

794
static int stat_to_v9stat(V9fsPDU *pdu, V9fsPath *name,
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
                            const struct stat *stbuf,
                            V9fsStat *v9stat)
{
    int err;
    const char *str;

    memset(v9stat, 0, sizeof(*v9stat));

    stat_to_qid(stbuf, &v9stat->qid);
    v9stat->mode = stat_to_v9mode(stbuf);
    v9stat->atime = stbuf->st_atime;
    v9stat->mtime = stbuf->st_mtime;
    v9stat->length = stbuf->st_size;

    v9fs_string_null(&v9stat->uid);
    v9fs_string_null(&v9stat->gid);
    v9fs_string_null(&v9stat->muid);

813 814 815
    v9stat->n_uid = stbuf->st_uid;
    v9stat->n_gid = stbuf->st_gid;
    v9stat->n_muid = 0;
816

817
    v9fs_string_null(&v9stat->extension);
818

819
    if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
820
        err = v9fs_co_readlink(pdu, name, &v9stat->extension);
821
        if (err < 0) {
822
            return err;
823
        }
824 825 826 827 828
    } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
        v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
                S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
                major(stbuf->st_rdev), minor(stbuf->st_rdev));
    } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
829 830
        v9fs_string_sprintf(&v9stat->extension, "%s %lu",
                "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink);
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
    }

    str = strrchr(name->data, '/');
    if (str) {
        str += 1;
    } else {
        str = name->data;
    }

    v9fs_string_sprintf(&v9stat->name, "%s", str);

    v9stat->size = 61 +
        v9fs_string_size(&v9stat->name) +
        v9fs_string_size(&v9stat->uid) +
        v9fs_string_size(&v9stat->gid) +
        v9fs_string_size(&v9stat->muid) +
        v9fs_string_size(&v9stat->extension);
    return 0;
}

851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
#define P9_STATS_MODE          0x00000001ULL
#define P9_STATS_NLINK         0x00000002ULL
#define P9_STATS_UID           0x00000004ULL
#define P9_STATS_GID           0x00000008ULL
#define P9_STATS_RDEV          0x00000010ULL
#define P9_STATS_ATIME         0x00000020ULL
#define P9_STATS_MTIME         0x00000040ULL
#define P9_STATS_CTIME         0x00000080ULL
#define P9_STATS_INO           0x00000100ULL
#define P9_STATS_SIZE          0x00000200ULL
#define P9_STATS_BLOCKS        0x00000400ULL

#define P9_STATS_BTIME         0x00000800ULL
#define P9_STATS_GEN           0x00001000ULL
#define P9_STATS_DATA_VERSION  0x00002000ULL

#define P9_STATS_BASIC         0x000007ffULL /* Mask for fields up to BLOCKS */
#define P9_STATS_ALL           0x00003fffULL /* Mask for All fields above */


static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
872
                                V9fsStatDotl *v9lstat)
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
{
    memset(v9lstat, 0, sizeof(*v9lstat));

    v9lstat->st_mode = stbuf->st_mode;
    v9lstat->st_nlink = stbuf->st_nlink;
    v9lstat->st_uid = stbuf->st_uid;
    v9lstat->st_gid = stbuf->st_gid;
    v9lstat->st_rdev = stbuf->st_rdev;
    v9lstat->st_size = stbuf->st_size;
    v9lstat->st_blksize = stbuf->st_blksize;
    v9lstat->st_blocks = stbuf->st_blocks;
    v9lstat->st_atime_sec = stbuf->st_atime;
    v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
    v9lstat->st_mtime_sec = stbuf->st_mtime;
    v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
    v9lstat->st_ctime_sec = stbuf->st_ctime;
    v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
    /* Currently we only support BASIC fields in stat */
    v9lstat->st_result_mask = P9_STATS_BASIC;

    stat_to_qid(stbuf, &v9lstat->qid);
}

896 897 898 899 900 901 902 903 904 905 906 907 908 909
static void print_sg(struct iovec *sg, int cnt)
{
    int i;

    printf("sg[%d]: {", cnt);
    for (i = 0; i < cnt; i++) {
        if (i) {
            printf(", ");
        }
        printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len);
    }
    printf("}\n");
}

910 911
/* Will call this only for path name based fid */
static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len)
912
{
913 914 915 916 917 918 919
    V9fsPath str;
    v9fs_path_init(&str);
    v9fs_path_copy(&str, dst);
    v9fs_string_sprintf((V9fsString *)dst, "%s%s", src->data, str.data+len);
    v9fs_path_free(&str);
    /* +1 to include terminating NULL */
    dst->size++;
920 921
}

922 923 924 925 926
static inline bool is_ro_export(FsContext *ctx)
{
    return ctx->export_flags & V9FS_RDONLY;
}

927
static void v9fs_version(void *opaque)
928
{
929
    ssize_t err;
930 931
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
932 933 934
    V9fsString version;
    size_t offset = 7;

935 936 937 938 939 940
    v9fs_string_init(&version);
    err = pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
    if (err < 0) {
        offset = err;
        goto out;
    }
941
    trace_v9fs_version(pdu->tag, pdu->id, s->msize, version.data);
942

943 944
    virtfs_reset(pdu);

945 946 947 948 949
    if (!strcmp(version.data, "9P2000.u")) {
        s->proto_version = V9FS_PROTO_2000U;
    } else if (!strcmp(version.data, "9P2000.L")) {
        s->proto_version = V9FS_PROTO_2000L;
    } else {
950
        v9fs_string_sprintf(&version, "unknown");
951
    }
952

953 954 955 956 957 958
    err = pdu_marshal(pdu, offset, "ds", s->msize, &version);
    if (err < 0) {
        offset = err;
        goto out;
    }
    offset += err;
959
    trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data);
960
out:
961
    pdu_complete(pdu, offset);
962
    v9fs_string_free(&version);
963 964
}

965
static void v9fs_attach(void *opaque)
966
{
967 968
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
969 970 971 972
    int32_t fid, afid, n_uname;
    V9fsString uname, aname;
    V9fsFidState *fidp;
    size_t offset = 7;
973
    V9fsQID qid;
974 975
    ssize_t err;

976 977 978 979 980 981 982
    v9fs_string_init(&uname);
    v9fs_string_init(&aname);
    err = pdu_unmarshal(pdu, offset, "ddssd", &fid,
                        &afid, &uname, &aname, &n_uname);
    if (err < 0) {
        goto out_nofid;
    }
983
    trace_v9fs_attach(pdu->tag, pdu->id, fid, afid, uname.data, aname.data);
984 985 986 987

    fidp = alloc_fid(s, fid);
    if (fidp == NULL) {
        err = -EINVAL;
988
        goto out_nofid;
989
    }
990
    fidp->uid = n_uname;
991
    err = v9fs_co_name_to_path(pdu, NULL, "/", &fidp->path);
992 993 994 995 996
    if (err < 0) {
        err = -EINVAL;
        clunk_fid(s, fid);
        goto out;
    }
997
    err = fid_to_qid(pdu, fidp, &qid);
998
    if (err < 0) {
999
        err = -EINVAL;
1000
        clunk_fid(s, fid);
1001 1002
        goto out;
    }
1003 1004 1005 1006 1007 1008
    err = pdu_marshal(pdu, offset, "Q", &qid);
    if (err < 0) {
        clunk_fid(s, fid);
        goto out;
    }
    err += offset;
1009 1010
    trace_v9fs_attach_return(pdu->tag, pdu->id,
                             qid.type, qid.version, qid.path);
1011 1012 1013 1014 1015 1016
    /*
     * disable migration if we haven't done already.
     * attach could get called multiple times for the same export.
     */
    if (!s->migration_blocker) {
        s->root_fid = fid;
1017 1018 1019
        error_setg(&s->migration_blocker,
                   "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'",
                   s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag);
1020 1021
        migrate_add_blocker(s->migration_blocker);
    }
1022
out:
1023
    put_fid(pdu, fidp);
1024
out_nofid:
1025
    pdu_complete(pdu, err);
1026 1027
    v9fs_string_free(&uname);
    v9fs_string_free(&aname);
1028 1029
}

1030
static void v9fs_stat(void *opaque)
1031
{
1032
    int32_t fid;
1033
    V9fsStat v9stat;
1034
    ssize_t err = 0;
1035 1036 1037 1038
    size_t offset = 7;
    struct stat stbuf;
    V9fsFidState *fidp;
    V9fsPDU *pdu = opaque;
1039

1040 1041 1042 1043
    err = pdu_unmarshal(pdu, offset, "d", &fid);
    if (err < 0) {
        goto out_nofid;
    }
1044
    trace_v9fs_stat(pdu->tag, pdu->id, fid);
1045

1046
    fidp = get_fid(pdu, fid);
1047
    if (fidp == NULL) {
1048
        err = -ENOENT;
1049
        goto out_nofid;
1050
    }
1051
    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
1052 1053 1054
    if (err < 0) {
        goto out;
    }
1055
    err = stat_to_v9stat(pdu, &fidp->path, &stbuf, &v9stat);
1056 1057 1058
    if (err < 0) {
        goto out;
    }
1059 1060 1061 1062 1063
    err = pdu_marshal(pdu, offset, "wS", 0, &v9stat);
    if (err < 0) {
        v9fs_stat_free(&v9stat);
        goto out;
    }
1064 1065
    trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode,
                           v9stat.atime, v9stat.mtime, v9stat.length);
1066
    err += offset;
1067
    v9fs_stat_free(&v9stat);
1068
out:
1069
    put_fid(pdu, fidp);
1070
out_nofid:
1071
    pdu_complete(pdu, err);
1072 1073
}

1074
static void v9fs_getattr(void *opaque)
1075 1076
{
    int32_t fid;
1077 1078 1079
    size_t offset = 7;
    ssize_t retval = 0;
    struct stat stbuf;
1080 1081
    V9fsFidState *fidp;
    uint64_t request_mask;
1082 1083 1084
    V9fsStatDotl v9stat_dotl;
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
1085

1086 1087 1088 1089
    retval = pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask);
    if (retval < 0) {
        goto out_nofid;
    }
1090
    trace_v9fs_getattr(pdu->tag, pdu->id, fid, request_mask);
1091

1092
    fidp = get_fid(pdu, fid);
1093
    if (fidp == NULL) {
1094
        retval = -ENOENT;
1095
        goto out_nofid;
1096
    }
1097 1098
    /*
     * Currently we only support BASIC fields in stat, so there is no
1099 1100
     * need to look at request_mask.
     */
1101
    retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
1102 1103 1104 1105
    if (retval < 0) {
        goto out;
    }
    stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl);
1106 1107 1108 1109

    /*  fill st_gen if requested and supported by underlying fs */
    if (request_mask & P9_STATS_GEN) {
        retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl);
1110 1111 1112 1113 1114 1115 1116
        switch (retval) {
        case 0:
            /* we have valid st_gen: update result mask */
            v9stat_dotl.st_result_mask |= P9_STATS_GEN;
            break;
        case -EINTR:
            /* request cancelled, e.g. by Tflush */
1117
            goto out;
1118 1119 1120
        default:
            /* failed to get st_gen: not fatal, ignore */
            break;
1121 1122
        }
    }
1123 1124 1125 1126 1127
    retval = pdu_marshal(pdu, offset, "A", &v9stat_dotl);
    if (retval < 0) {
        goto out;
    }
    retval += offset;
1128 1129 1130
    trace_v9fs_getattr_return(pdu->tag, pdu->id, v9stat_dotl.st_result_mask,
                              v9stat_dotl.st_mode, v9stat_dotl.st_uid,
                              v9stat_dotl.st_gid);
1131 1132 1133
out:
    put_fid(pdu, fidp);
out_nofid:
1134
    pdu_complete(pdu, retval);
1135 1136
}

1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
/* Attribute flags */
#define P9_ATTR_MODE       (1 << 0)
#define P9_ATTR_UID        (1 << 1)
#define P9_ATTR_GID        (1 << 2)
#define P9_ATTR_SIZE       (1 << 3)
#define P9_ATTR_ATIME      (1 << 4)
#define P9_ATTR_MTIME      (1 << 5)
#define P9_ATTR_CTIME      (1 << 6)
#define P9_ATTR_ATIME_SET  (1 << 7)
#define P9_ATTR_MTIME_SET  (1 << 8)

#define P9_ATTR_MASK    127
1149

1150
static void v9fs_setattr(void *opaque)
1151
{
1152 1153 1154 1155 1156 1157
    int err = 0;
    int32_t fid;
    V9fsFidState *fidp;
    size_t offset = 7;
    V9fsIattr v9iattr;
    V9fsPDU *pdu = opaque;
1158

1159 1160 1161 1162
    err = pdu_unmarshal(pdu, offset, "dI", &fid, &v9iattr);
    if (err < 0) {
        goto out_nofid;
    }
1163

1164
    fidp = get_fid(pdu, fid);
1165 1166
    if (fidp == NULL) {
        err = -EINVAL;
1167
        goto out_nofid;
1168
    }
1169
    if (v9iattr.valid & P9_ATTR_MODE) {
1170
        err = v9fs_co_chmod(pdu, &fidp->path, v9iattr.mode);
1171 1172
        if (err < 0) {
            goto out;
1173 1174
        }
    }
1175
    if (v9iattr.valid & (P9_ATTR_ATIME | P9_ATTR_MTIME)) {
1176
        struct timespec times[2];
1177 1178
        if (v9iattr.valid & P9_ATTR_ATIME) {
            if (v9iattr.valid & P9_ATTR_ATIME_SET) {
1179 1180
                times[0].tv_sec = v9iattr.atime_sec;
                times[0].tv_nsec = v9iattr.atime_nsec;
1181 1182 1183 1184 1185 1186
            } else {
                times[0].tv_nsec = UTIME_NOW;
            }
        } else {
            times[0].tv_nsec = UTIME_OMIT;
        }
1187 1188
        if (v9iattr.valid & P9_ATTR_MTIME) {
            if (v9iattr.valid & P9_ATTR_MTIME_SET) {
1189 1190
                times[1].tv_sec = v9iattr.mtime_sec;
                times[1].tv_nsec = v9iattr.mtime_nsec;
1191 1192 1193 1194 1195 1196
            } else {
                times[1].tv_nsec = UTIME_NOW;
            }
        } else {
            times[1].tv_nsec = UTIME_OMIT;
        }
1197
        err = v9fs_co_utimensat(pdu, &fidp->path, times);
1198 1199 1200
        if (err < 0) {
            goto out;
        }
1201
    }
1202 1203 1204 1205
    /*
     * If the only valid entry in iattr is ctime we can call
     * chown(-1,-1) to update the ctime of the file
     */
1206 1207 1208 1209
    if ((v9iattr.valid & (P9_ATTR_UID | P9_ATTR_GID)) ||
        ((v9iattr.valid & P9_ATTR_CTIME)
         && !((v9iattr.valid & P9_ATTR_MASK) & ~P9_ATTR_CTIME))) {
        if (!(v9iattr.valid & P9_ATTR_UID)) {
1210 1211
            v9iattr.uid = -1;
        }
1212
        if (!(v9iattr.valid & P9_ATTR_GID)) {
1213 1214
            v9iattr.gid = -1;
        }
1215
        err = v9fs_co_chown(pdu, &fidp->path, v9iattr.uid,
1216 1217 1218 1219
                            v9iattr.gid);
        if (err < 0) {
            goto out;
        }
1220
    }
1221
    if (v9iattr.valid & (P9_ATTR_SIZE)) {
1222
        err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size);
1223 1224 1225
        if (err < 0) {
            goto out;
        }
1226
    }
1227
    err = offset;
1228
out:
1229
    put_fid(pdu, fidp);
1230
out_nofid:
1231
    pdu_complete(pdu, err);
1232 1233
}

1234
static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids)
1235 1236
{
    int i;
1237
    ssize_t err;
1238
    size_t offset = 7;
1239 1240 1241 1242 1243 1244

    err = pdu_marshal(pdu, offset, "w", nwnames);
    if (err < 0) {
        return err;
    }
    offset += err;
1245
    for (i = 0; i < nwnames; i++) {
1246 1247 1248 1249 1250
        err = pdu_marshal(pdu, offset, "Q", &qids[i]);
        if (err < 0) {
            return err;
        }
        offset += err;
1251
    }
1252
    return offset;
1253 1254
}

1255
static void v9fs_walk(void *opaque)
1256
{
1257 1258 1259
    int name_idx;
    V9fsQID *qids = NULL;
    int i, err = 0;
1260
    V9fsPath dpath, path;
1261 1262 1263 1264 1265 1266
    uint16_t nwnames;
    struct stat stbuf;
    size_t offset = 7;
    int32_t fid, newfid;
    V9fsString *wnames = NULL;
    V9fsFidState *fidp;
1267
    V9fsFidState *newfidp = NULL;
1268 1269
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
1270

1271 1272
    err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames);
    if (err < 0) {
1273
        pdu_complete(pdu, err);
1274 1275 1276
        return ;
    }
    offset += err;
1277

1278 1279
    trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames);

1280 1281 1282 1283
    if (nwnames && nwnames <= P9_MAXWELEM) {
        wnames = g_malloc0(sizeof(wnames[0]) * nwnames);
        qids   = g_malloc0(sizeof(qids[0]) * nwnames);
        for (i = 0; i < nwnames; i++) {
1284 1285 1286 1287 1288
            err = pdu_unmarshal(pdu, offset, "s", &wnames[i]);
            if (err < 0) {
                goto out_nofid;
            }
            offset += err;
1289
        }
1290
    } else if (nwnames > P9_MAXWELEM) {
1291
        err = -EINVAL;
1292
        goto out_nofid;
1293
    }
1294
    fidp = get_fid(pdu, fid);
1295
    if (fidp == NULL) {
1296
        err = -ENOENT;
1297
        goto out_nofid;
1298
    }
1299 1300 1301 1302 1303 1304 1305 1306 1307
    v9fs_path_init(&dpath);
    v9fs_path_init(&path);
    /*
     * Both dpath and path initially poin to fidp.
     * Needed to handle request with nwnames == 0
     */
    v9fs_path_copy(&dpath, &fidp->path);
    v9fs_path_copy(&path, &fidp->path);
    for (name_idx = 0; name_idx < nwnames; name_idx++) {
1308
        err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path);
1309 1310 1311
        if (err < 0) {
            goto out;
        }
1312
        err = v9fs_co_lstat(pdu, &path, &stbuf);
1313 1314 1315 1316 1317 1318
        if (err < 0) {
            goto out;
        }
        stat_to_qid(&stbuf, &qids[name_idx]);
        v9fs_path_copy(&dpath, &path);
    }
1319
    if (fid == newfid) {
1320
        BUG_ON(fidp->fid_type != P9_FID_NONE);
1321
        v9fs_path_copy(&fidp->path, &path);
1322
    } else {
1323 1324
        newfidp = alloc_fid(s, newfid);
        if (newfidp == NULL) {
1325 1326 1327
            err = -EINVAL;
            goto out;
        }
1328
        newfidp->uid = fidp->uid;
1329
        v9fs_path_copy(&newfidp->path, &path);
1330
    }
1331
    err = v9fs_walk_marshal(pdu, nwnames, qids);
1332
    trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids);
1333
out:
1334
    put_fid(pdu, fidp);
1335
    if (newfidp) {
1336
        put_fid(pdu, newfidp);
1337
    }
1338 1339
    v9fs_path_free(&dpath);
    v9fs_path_free(&path);
1340
out_nofid:
1341
    pdu_complete(pdu, err);
1342 1343 1344 1345 1346 1347 1348
    if (nwnames && nwnames <= P9_MAXWELEM) {
        for (name_idx = 0; name_idx < nwnames; name_idx++) {
            v9fs_string_free(&wnames[name_idx]);
        }
        g_free(wnames);
        g_free(qids);
    }
1349 1350
}

1351
static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path)
1352 1353 1354
{
    struct statfs stbuf;
    int32_t iounit = 0;
1355
    V9fsState *s = pdu->s;
1356 1357 1358 1359 1360

    /*
     * iounit should be multiples of f_bsize (host filesystem block size
     * and as well as less than (client msize - P9_IOHDRSZ))
     */
1361
    if (!v9fs_co_statfs(pdu, path, &stbuf)) {
1362 1363 1364 1365 1366 1367 1368 1369 1370
        iounit = stbuf.f_bsize;
        iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
    }
    if (!iounit) {
        iounit = s->msize - P9_IOHDRSZ;
    }
    return iounit;
}

1371
static void v9fs_open(void *opaque)
1372
{
1373 1374 1375 1376
    int flags;
    int32_t fid;
    int32_t mode;
    V9fsQID qid;
1377
    int iounit = 0;
1378 1379 1380 1381 1382 1383
    ssize_t err = 0;
    size_t offset = 7;
    struct stat stbuf;
    V9fsFidState *fidp;
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
1384

1385
    if (s->proto_version == V9FS_PROTO_2000L) {
1386
        err = pdu_unmarshal(pdu, offset, "dd", &fid, &mode);
1387
    } else {
1388 1389 1390
        uint8_t modebyte;
        err = pdu_unmarshal(pdu, offset, "db", &fid, &modebyte);
        mode = modebyte;
1391 1392 1393
    }
    if (err < 0) {
        goto out_nofid;
1394
    }
1395 1396
    trace_v9fs_open(pdu->tag, pdu->id, fid, mode);

1397
    fidp = get_fid(pdu, fid);
1398 1399
    if (fidp == NULL) {
        err = -ENOENT;
1400
        goto out_nofid;
1401
    }
1402
    BUG_ON(fidp->fid_type != P9_FID_NONE);
1403

1404
    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
1405
    if (err < 0) {
1406 1407
        goto out;
    }
1408 1409
    stat_to_qid(&stbuf, &qid);
    if (S_ISDIR(stbuf.st_mode)) {
1410
        err = v9fs_co_opendir(pdu, fidp);
1411 1412 1413 1414
        if (err < 0) {
            goto out;
        }
        fidp->fid_type = P9_FID_DIR;
1415 1416 1417 1418 1419
        err = pdu_marshal(pdu, offset, "Qd", &qid, 0);
        if (err < 0) {
            goto out;
        }
        err += offset;
1420
    } else {
1421
        if (s->proto_version == V9FS_PROTO_2000L) {
1422
            flags = get_dotl_openflags(s, mode);
1423
        } else {
1424
            flags = omode_to_uflags(mode);
1425
        }
1426 1427 1428 1429 1430 1431 1432
        if (is_ro_export(&s->ctx)) {
            if (mode & O_WRONLY || mode & O_RDWR ||
                mode & O_APPEND || mode & O_TRUNC) {
                err = -EROFS;
                goto out;
            }
        }
1433
        err = v9fs_co_open(pdu, fidp, flags);
1434 1435 1436 1437
        if (err < 0) {
            goto out;
        }
        fidp->fid_type = P9_FID_FILE;
1438 1439 1440 1441 1442 1443 1444 1445
        fidp->open_flags = flags;
        if (flags & O_EXCL) {
            /*
             * We let the host file system do O_EXCL check
             * We should not reclaim such fd
             */
            fidp->flags |= FID_NON_RECLAIMABLE;
        }
1446
        iounit = get_iounit(pdu, &fidp->path);
1447 1448 1449 1450 1451
        err = pdu_marshal(pdu, offset, "Qd", &qid, iounit);
        if (err < 0) {
            goto out;
        }
        err += offset;
1452
    }
1453 1454
    trace_v9fs_open_return(pdu->tag, pdu->id,
                           qid.type, qid.version, qid.path, iounit);
1455
out:
1456
    put_fid(pdu, fidp);
1457
out_nofid:
1458
    pdu_complete(pdu, err);
1459 1460
}

1461
static void v9fs_lcreate(void *opaque)
1462 1463 1464 1465
{
    int32_t dfid, flags, mode;
    gid_t gid;
    ssize_t err = 0;
1466 1467 1468 1469 1470 1471 1472
    ssize_t offset = 7;
    V9fsString name;
    V9fsFidState *fidp;
    struct stat stbuf;
    V9fsQID qid;
    int32_t iounit;
    V9fsPDU *pdu = opaque;
1473

1474 1475 1476 1477 1478 1479
    v9fs_string_init(&name);
    err = pdu_unmarshal(pdu, offset, "dsddd", &dfid,
                        &name, &flags, &mode, &gid);
    if (err < 0) {
        goto out_nofid;
    }
1480
    trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid);
1481

1482
    fidp = get_fid(pdu, dfid);
1483
    if (fidp == NULL) {
1484
        err = -ENOENT;
1485
        goto out_nofid;
1486 1487
    }

1488
    flags = get_dotl_openflags(pdu->s, flags);
1489
    err = v9fs_co_open2(pdu, fidp, &name, gid,
1490
                        flags | O_CREAT, mode, &stbuf);
1491 1492 1493 1494
    if (err < 0) {
        goto out;
    }
    fidp->fid_type = P9_FID_FILE;
1495 1496 1497 1498 1499 1500 1501 1502
    fidp->open_flags = flags;
    if (flags & O_EXCL) {
        /*
         * We let the host file system do O_EXCL check
         * We should not reclaim such fd
         */
        fidp->flags |= FID_NON_RECLAIMABLE;
    }
1503
    iounit =  get_iounit(pdu, &fidp->path);
1504
    stat_to_qid(&stbuf, &qid);
1505 1506 1507 1508 1509
    err = pdu_marshal(pdu, offset, "Qd", &qid, iounit);
    if (err < 0) {
        goto out;
    }
    err += offset;
1510 1511
    trace_v9fs_lcreate_return(pdu->tag, pdu->id,
                              qid.type, qid.version, qid.path, iounit);
1512
out:
1513
    put_fid(pdu, fidp);
1514
out_nofid:
1515
    pdu_complete(pdu, err);
1516
    v9fs_string_free(&name);
1517 1518
}

1519
static void v9fs_fsync(void *opaque)
1520
{
1521
    int err;
1522
    int32_t fid;
1523
    int datasync;
1524 1525
    size_t offset = 7;
    V9fsFidState *fidp;
1526
    V9fsPDU *pdu = opaque;
1527

1528 1529 1530 1531
    err = pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
    if (err < 0) {
        goto out_nofid;
    }
1532 1533
    trace_v9fs_fsync(pdu->tag, pdu->id, fid, datasync);

1534
    fidp = get_fid(pdu, fid);
1535 1536
    if (fidp == NULL) {
        err = -ENOENT;
1537
        goto out_nofid;
1538
    }
1539
    err = v9fs_co_fsync(pdu, fidp, datasync);
1540 1541 1542
    if (!err) {
        err = offset;
    }
1543
    put_fid(pdu, fidp);
1544
out_nofid:
1545
    pdu_complete(pdu, err);
1546 1547
}

1548
static void v9fs_clunk(void *opaque)
1549
{
1550
    int err;
1551 1552
    int32_t fid;
    size_t offset = 7;
1553
    V9fsFidState *fidp;
1554 1555
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
1556

1557 1558 1559 1560
    err = pdu_unmarshal(pdu, offset, "d", &fid);
    if (err < 0) {
        goto out_nofid;
    }
1561
    trace_v9fs_clunk(pdu->tag, pdu->id, fid);
1562

1563
    fidp = clunk_fid(s, fid);
1564 1565 1566 1567
    if (fidp == NULL) {
        err = -ENOENT;
        goto out_nofid;
    }
1568 1569 1570 1571 1572
    /*
     * Bump the ref so that put_fid will
     * free the fid.
     */
    fidp->ref++;
1573 1574 1575 1576
    err = put_fid(pdu, fidp);
    if (!err) {
        err = offset;
    }
1577
out_nofid:
1578
    pdu_complete(pdu, err);
1579 1580
}

1581 1582
static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
                           uint64_t off, uint32_t max_count)
1583
{
1584
    ssize_t err;
1585 1586 1587
    size_t offset = 7;
    int read_count;
    int64_t xattr_len;
W
Wei Liu 已提交
1588 1589
    V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
    VirtQueueElement *elem = &v->elems[pdu->idx];
1590

1591 1592 1593 1594 1595 1596 1597 1598 1599
    xattr_len = fidp->fs.xattr.len;
    read_count = xattr_len - off;
    if (read_count > max_count) {
        read_count = max_count;
    } else if (read_count < 0) {
        /*
         * read beyond XATTR value
         */
        read_count = 0;
1600
    }
1601 1602 1603 1604 1605
    err = pdu_marshal(pdu, offset, "d", read_count);
    if (err < 0) {
        return err;
    }
    offset += err;
W
Wei Liu 已提交
1606 1607

    err = v9fs_pack(elem->in_sg, elem->in_num, offset,
1608 1609 1610 1611 1612 1613
                    ((char *)fidp->fs.xattr.value) + off,
                    read_count);
    if (err < 0) {
        return err;
    }
    offset += err;
1614
    return offset;
1615 1616
}

1617
static int v9fs_do_readdir_with_stat(V9fsPDU *pdu,
1618
                                     V9fsFidState *fidp, uint32_t max_count)
1619
{
1620
    V9fsPath path;
1621 1622 1623 1624 1625
    V9fsStat v9stat;
    int len, err = 0;
    int32_t count = 0;
    struct stat stbuf;
    off_t saved_dir_pos;
1626
    struct dirent *dent, *result;
1627

1628
    /* save the directory position */
1629
    saved_dir_pos = v9fs_co_telldir(pdu, fidp);
1630 1631
    if (saved_dir_pos < 0) {
        return saved_dir_pos;
1632
    }
1633 1634 1635

    dent = g_malloc(sizeof(struct dirent));

1636
    while (1) {
1637
        v9fs_path_init(&path);
1638
        err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
1639
        if (err || !result) {
1640 1641
            break;
        }
1642
        err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path);
1643 1644 1645
        if (err < 0) {
            goto out;
        }
1646
        err = v9fs_co_lstat(pdu, &path, &stbuf);
1647 1648 1649
        if (err < 0) {
            goto out;
        }
1650
        err = stat_to_v9stat(pdu, &path, &stbuf, &v9stat);
1651 1652
        if (err < 0) {
            goto out;
1653
        }
1654 1655 1656 1657
        /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
        len = pdu_marshal(pdu, 11 + count, "S", &v9stat);
        if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) {
            /* Ran out of buffer. Set dir back to old position and return */
1658
            v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
1659
            v9fs_stat_free(&v9stat);
1660
            v9fs_path_free(&path);
1661
            g_free(dent);
1662 1663 1664 1665
            return count;
        }
        count += len;
        v9fs_stat_free(&v9stat);
1666
        v9fs_path_free(&path);
1667
        saved_dir_pos = dent->d_off;
1668 1669
    }
out:
1670
    g_free(dent);
1671
    v9fs_path_free(&path);
1672 1673
    if (err < 0) {
        return err;
1674
    }
1675
    return count;
1676 1677
}

1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689
/*
 * Create a QEMUIOVector for a sub-region of PDU iovecs
 *
 * @qiov:       uninitialized QEMUIOVector
 * @skip:       number of bytes to skip from beginning of PDU
 * @size:       number of bytes to include
 * @is_write:   true - write, false - read
 *
 * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up
 * with qemu_iovec_destroy().
 */
static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu,
1690
                                    size_t skip, size_t size,
1691 1692 1693 1694 1695 1696
                                    bool is_write)
{
    QEMUIOVector elem;
    struct iovec *iov;
    unsigned int niov;

1697
    virtio_init_iov_from_pdu(pdu, &iov, &niov, is_write);
1698 1699 1700

    qemu_iovec_init_external(&elem, iov, niov);
    qemu_iovec_init(qiov, niov);
1701
    qemu_iovec_concat(qiov, &elem, skip, size);
1702 1703
}

1704
static void v9fs_read(void *opaque)
1705
{
1706
    int32_t fid;
1707
    uint64_t off;
1708
    ssize_t err = 0;
1709 1710
    int32_t count = 0;
    size_t offset = 7;
1711
    uint32_t max_count;
1712 1713 1714
    V9fsFidState *fidp;
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
1715

1716 1717 1718 1719
    err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &max_count);
    if (err < 0) {
        goto out_nofid;
    }
1720
    trace_v9fs_read(pdu->tag, pdu->id, fid, off, max_count);
1721

1722
    fidp = get_fid(pdu, fid);
1723
    if (fidp == NULL) {
1724
        err = -EINVAL;
1725
        goto out_nofid;
1726
    }
1727
    if (fidp->fid_type == P9_FID_DIR) {
1728

1729
        if (off == 0) {
1730
            v9fs_co_rewinddir(pdu, fidp);
1731
        }
1732
        count = v9fs_do_readdir_with_stat(pdu, fidp, max_count);
1733 1734 1735
        if (count < 0) {
            err = count;
            goto out;
1736
        }
1737 1738 1739 1740 1741
        err = pdu_marshal(pdu, offset, "d", count);
        if (err < 0) {
            goto out;
        }
        err += offset + count;
1742
    } else if (fidp->fid_type == P9_FID_FILE) {
1743 1744
        QEMUIOVector qiov_full;
        QEMUIOVector qiov;
1745 1746
        int32_t len;

1747 1748
        v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset + 4, max_count, false);
        qemu_iovec_init(&qiov, qiov_full.niov);
1749
        do {
1750
            qemu_iovec_reset(&qiov);
1751
            qemu_iovec_concat(&qiov, &qiov_full, count, qiov_full.size - count);
1752
            if (0) {
1753
                print_sg(qiov.iov, qiov.niov);
1754 1755 1756
            }
            /* Loop in case of EINTR */
            do {
1757
                len = v9fs_co_preadv(pdu, fidp, qiov.iov, qiov.niov, off);
1758 1759 1760 1761
                if (len >= 0) {
                    off   += len;
                    count += len;
                }
1762
            } while (len == -EINTR && !pdu->cancelled);
1763 1764 1765 1766 1767 1768
            if (len < 0) {
                /* IO error return the error */
                err = len;
                goto out;
            }
        } while (count < max_count && len > 0);
1769 1770 1771 1772 1773
        err = pdu_marshal(pdu, offset, "d", count);
        if (err < 0) {
            goto out;
        }
        err += offset + count;
1774 1775
        qemu_iovec_destroy(&qiov);
        qemu_iovec_destroy(&qiov_full);
1776 1777
    } else if (fidp->fid_type == P9_FID_XATTR) {
        err = v9fs_xattr_read(s, pdu, fidp, off, max_count);
1778 1779
    } else {
        err = -EINVAL;
1780
    }
1781
    trace_v9fs_read_return(pdu->tag, pdu->id, count, err);
1782
out:
1783
    put_fid(pdu, fidp);
1784
out_nofid:
1785
    pdu_complete(pdu, err);
1786 1787
}

1788
static size_t v9fs_readdir_data_size(V9fsString *name)
1789
{
1790 1791 1792 1793 1794
    /*
     * Size of each dirent on the wire: size of qid (13) + size of offset (8)
     * size of type (1) + size of name.size (2) + strlen(name.data)
     */
    return 24 + v9fs_string_size(name);
1795 1796
}

1797
static int v9fs_do_readdir(V9fsPDU *pdu,
1798
                           V9fsFidState *fidp, int32_t max_count)
1799 1800
{
    size_t size;
1801 1802 1803 1804 1805
    V9fsQID qid;
    V9fsString name;
    int len, err = 0;
    int32_t count = 0;
    off_t saved_dir_pos;
1806
    struct dirent *dent, *result;
1807

1808
    /* save the directory position */
1809
    saved_dir_pos = v9fs_co_telldir(pdu, fidp);
1810 1811 1812
    if (saved_dir_pos < 0) {
        return saved_dir_pos;
    }
1813 1814 1815

    dent = g_malloc(sizeof(struct dirent));

1816
    while (1) {
1817
        err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
1818
        if (err || !result) {
1819 1820 1821 1822 1823
            break;
        }
        v9fs_string_init(&name);
        v9fs_string_sprintf(&name, "%s", dent->d_name);
        if ((count + v9fs_readdir_data_size(&name)) > max_count) {
1824
            /* Ran out of buffer. Set dir back to old position and return */
1825
            v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
1826
            v9fs_string_free(&name);
1827
            g_free(dent);
1828
            return count;
1829
        }
1830 1831
        /*
         * Fill up just the path field of qid because the client uses
1832 1833 1834
         * only that. To fill the entire qid structure we will have
         * to stat each dirent found, which is expensive
         */
1835 1836
        size = MIN(sizeof(dent->d_ino), sizeof(qid.path));
        memcpy(&qid.path, &dent->d_ino, size);
1837
        /* Fill the other fields with dummy values */
1838 1839
        qid.type = 0;
        qid.version = 0;
1840

1841 1842 1843 1844
        /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
        len = pdu_marshal(pdu, 11 + count, "Qqbs",
                          &qid, dent->d_off,
                          dent->d_type, &name);
1845 1846 1847 1848 1849 1850
        if (len < 0) {
            v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
            v9fs_string_free(&name);
            g_free(dent);
            return len;
        }
1851 1852 1853 1854
        count += len;
        v9fs_string_free(&name);
        saved_dir_pos = dent->d_off;
    }
1855
    g_free(dent);
1856 1857 1858 1859
    if (err < 0) {
        return err;
    }
    return count;
1860 1861
}

1862
static void v9fs_readdir(void *opaque)
1863 1864
{
    int32_t fid;
1865 1866
    V9fsFidState *fidp;
    ssize_t retval = 0;
1867
    size_t offset = 7;
1868 1869 1870
    uint64_t initial_offset;
    int32_t count;
    uint32_t max_count;
1871
    V9fsPDU *pdu = opaque;
1872

1873 1874 1875 1876 1877
    retval = pdu_unmarshal(pdu, offset, "dqd", &fid,
                           &initial_offset, &max_count);
    if (retval < 0) {
        goto out_nofid;
    }
1878 1879
    trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count);

1880
    fidp = get_fid(pdu, fid);
1881 1882 1883 1884 1885
    if (fidp == NULL) {
        retval = -EINVAL;
        goto out_nofid;
    }
    if (!fidp->fs.dir) {
1886
        retval = -EINVAL;
1887 1888
        goto out;
    }
1889
    if (initial_offset == 0) {
1890
        v9fs_co_rewinddir(pdu, fidp);
1891
    } else {
1892
        v9fs_co_seekdir(pdu, fidp, initial_offset);
1893
    }
1894
    count = v9fs_do_readdir(pdu, fidp, max_count);
1895 1896 1897 1898
    if (count < 0) {
        retval = count;
        goto out;
    }
1899 1900 1901 1902 1903
    retval = pdu_marshal(pdu, offset, "d", count);
    if (retval < 0) {
        goto out;
    }
    retval += count + offset;
1904
    trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval);
1905
out:
1906
    put_fid(pdu, fidp);
1907
out_nofid:
1908
    pdu_complete(pdu, retval);
1909 1910
}

1911
static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
1912
                            uint64_t off, uint32_t count,
1913
                            struct iovec *sg, int cnt)
1914 1915 1916 1917 1918
{
    int i, to_copy;
    ssize_t err = 0;
    int write_count;
    int64_t xattr_len;
1919
    size_t offset = 7;
1920

1921 1922 1923 1924 1925

    xattr_len = fidp->fs.xattr.len;
    write_count = xattr_len - off;
    if (write_count > count) {
        write_count = count;
1926 1927 1928 1929 1930 1931 1932 1933
    } else if (write_count < 0) {
        /*
         * write beyond XATTR value len specified in
         * xattrcreate
         */
        err = -ENOSPC;
        goto out;
    }
1934 1935 1936 1937 1938
    err = pdu_marshal(pdu, offset, "d", write_count);
    if (err < 0) {
        return err;
    }
    err += offset;
1939
    fidp->fs.xattr.copied_len += write_count;
1940 1941 1942
    /*
     * Now copy the content from sg list
     */
1943 1944 1945
    for (i = 0; i < cnt; i++) {
        if (write_count > sg[i].iov_len) {
            to_copy = sg[i].iov_len;
1946 1947 1948
        } else {
            to_copy = write_count;
        }
1949
        memcpy((char *)fidp->fs.xattr.value + off, sg[i].iov_base, to_copy);
1950
        /* updating vs->off since we are not using below */
1951
        off += to_copy;
1952 1953 1954
        write_count -= to_copy;
    }
out:
1955
    return err;
1956 1957
}

1958
static void v9fs_write(void *opaque)
1959
{
1960 1961
    ssize_t err;
    int32_t fid;
1962 1963
    uint64_t off;
    uint32_t count;
1964 1965 1966 1967
    int32_t len = 0;
    int32_t total = 0;
    size_t offset = 7;
    V9fsFidState *fidp;
1968 1969
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
1970 1971
    QEMUIOVector qiov_full;
    QEMUIOVector qiov;
1972

1973 1974
    err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &count);
    if (err < 0) {
1975
        pdu_complete(pdu, err);
S
Stefan Weil 已提交
1976
        return;
1977 1978
    }
    offset += err;
1979 1980
    v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, count, true);
    trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, qiov_full.niov);
1981

1982
    fidp = get_fid(pdu, fid);
1983
    if (fidp == NULL) {
1984
        err = -EINVAL;
1985
        goto out_nofid;
1986
    }
1987 1988
    if (fidp->fid_type == P9_FID_FILE) {
        if (fidp->fs.fd == -1) {
1989 1990 1991
            err = -EINVAL;
            goto out;
        }
1992
    } else if (fidp->fid_type == P9_FID_XATTR) {
1993 1994 1995
        /*
         * setxattr operation
         */
1996 1997
        err = v9fs_xattr_write(s, pdu, fidp, off, count,
                               qiov_full.iov, qiov_full.niov);
1998
        goto out;
1999
    } else {
2000 2001 2002
        err = -EINVAL;
        goto out;
    }
2003
    qemu_iovec_init(&qiov, qiov_full.niov);
2004
    do {
2005
        qemu_iovec_reset(&qiov);
2006
        qemu_iovec_concat(&qiov, &qiov_full, total, qiov_full.size - total);
2007
        if (0) {
2008
            print_sg(qiov.iov, qiov.niov);
2009
        }
2010 2011
        /* Loop in case of EINTR */
        do {
2012
            len = v9fs_co_pwritev(pdu, fidp, qiov.iov, qiov.niov, off);
2013 2014 2015 2016
            if (len >= 0) {
                off   += len;
                total += len;
            }
2017
        } while (len == -EINTR && !pdu->cancelled);
2018 2019 2020
        if (len < 0) {
            /* IO error return the error */
            err = len;
2021
            goto out_qiov;
2022 2023
        }
    } while (total < count && len > 0);
2024 2025

    offset = 7;
2026 2027 2028 2029 2030
    err = pdu_marshal(pdu, offset, "d", total);
    if (err < 0) {
        goto out;
    }
    err += offset;
2031
    trace_v9fs_write_return(pdu->tag, pdu->id, total, err);
2032 2033
out_qiov:
    qemu_iovec_destroy(&qiov);
2034
out:
2035
    put_fid(pdu, fidp);
2036
out_nofid:
2037
    qemu_iovec_destroy(&qiov_full);
2038
    pdu_complete(pdu, err);
2039 2040
}

2041
static void v9fs_create(void *opaque)
2042
{
2043 2044 2045 2046 2047 2048 2049
    int32_t fid;
    int err = 0;
    size_t offset = 7;
    V9fsFidState *fidp;
    V9fsQID qid;
    int32_t perm;
    int8_t mode;
2050
    V9fsPath path;
2051 2052 2053 2054 2055
    struct stat stbuf;
    V9fsString name;
    V9fsString extension;
    int iounit;
    V9fsPDU *pdu = opaque;
2056

2057
    v9fs_path_init(&path);
2058 2059 2060 2061 2062 2063 2064
    v9fs_string_init(&name);
    v9fs_string_init(&extension);
    err = pdu_unmarshal(pdu, offset, "dsdbs", &fid, &name,
                        &perm, &mode, &extension);
    if (err < 0) {
        goto out_nofid;
    }
2065 2066
    trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode);

2067
    fidp = get_fid(pdu, fid);
2068 2069
    if (fidp == NULL) {
        err = -EINVAL;
2070
        goto out_nofid;
2071
    }
2072
    if (perm & P9_STAT_MODE_DIR) {
2073
        err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777,
2074
                            fidp->uid, -1, &stbuf);
2075 2076 2077
        if (err < 0) {
            goto out;
        }
2078
        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
2079 2080 2081 2082
        if (err < 0) {
            goto out;
        }
        v9fs_path_copy(&fidp->path, &path);
2083
        err = v9fs_co_opendir(pdu, fidp);
2084 2085 2086 2087 2088
        if (err < 0) {
            goto out;
        }
        fidp->fid_type = P9_FID_DIR;
    } else if (perm & P9_STAT_MODE_SYMLINK) {
2089
        err = v9fs_co_symlink(pdu, fidp, &name,
2090
                              extension.data, -1 , &stbuf);
2091 2092 2093
        if (err < 0) {
            goto out;
        }
2094
        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
2095 2096 2097 2098
        if (err < 0) {
            goto out;
        }
        v9fs_path_copy(&fidp->path, &path);
2099
    } else if (perm & P9_STAT_MODE_LINK) {
2100
        int32_t ofid = atoi(extension.data);
2101
        V9fsFidState *ofidp = get_fid(pdu, ofid);
2102
        if (ofidp == NULL) {
2103 2104 2105
            err = -EINVAL;
            goto out;
        }
2106 2107
        err = v9fs_co_link(pdu, ofidp, fidp, &name);
        put_fid(pdu, ofidp);
2108 2109 2110
        if (err < 0) {
            goto out;
        }
2111
        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
2112
        if (err < 0) {
2113
            fidp->fid_type = P9_FID_NONE;
2114
            goto out;
2115
        }
2116
        v9fs_path_copy(&fidp->path, &path);
2117
        err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
2118 2119 2120 2121
        if (err < 0) {
            fidp->fid_type = P9_FID_NONE;
            goto out;
        }
2122
    } else if (perm & P9_STAT_MODE_DEVICE) {
2123 2124 2125 2126
        char ctype;
        uint32_t major, minor;
        mode_t nmode = 0;

2127
        if (sscanf(extension.data, "%c %u %u", &ctype, &major, &minor) != 3) {
2128
            err = -errno;
2129
            goto out;
2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140
        }

        switch (ctype) {
        case 'c':
            nmode = S_IFCHR;
            break;
        case 'b':
            nmode = S_IFBLK;
            break;
        default:
            err = -EIO;
2141 2142
            goto out;
        }
2143

2144
        nmode |= perm & 0777;
2145
        err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1,
2146
                            makedev(major, minor), nmode, &stbuf);
2147 2148 2149
        if (err < 0) {
            goto out;
        }
2150
        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
2151 2152 2153 2154
        if (err < 0) {
            goto out;
        }
        v9fs_path_copy(&fidp->path, &path);
2155
    } else if (perm & P9_STAT_MODE_NAMED_PIPE) {
2156
        err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1,
2157
                            0, S_IFIFO | (perm & 0777), &stbuf);
2158 2159 2160
        if (err < 0) {
            goto out;
        }
2161
        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
2162 2163 2164 2165
        if (err < 0) {
            goto out;
        }
        v9fs_path_copy(&fidp->path, &path);
2166
    } else if (perm & P9_STAT_MODE_SOCKET) {
2167
        err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1,
2168
                            0, S_IFSOCK | (perm & 0777), &stbuf);
2169 2170 2171
        if (err < 0) {
            goto out;
        }
2172
        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
2173 2174 2175 2176
        if (err < 0) {
            goto out;
        }
        v9fs_path_copy(&fidp->path, &path);
2177
    } else {
2178
        err = v9fs_co_open2(pdu, fidp, &name, -1,
2179
                            omode_to_uflags(mode)|O_CREAT, perm, &stbuf);
2180 2181 2182 2183
        if (err < 0) {
            goto out;
        }
        fidp->fid_type = P9_FID_FILE;
2184 2185 2186 2187 2188 2189 2190 2191
        fidp->open_flags = omode_to_uflags(mode);
        if (fidp->open_flags & O_EXCL) {
            /*
             * We let the host file system do O_EXCL check
             * We should not reclaim such fd
             */
            fidp->flags |= FID_NON_RECLAIMABLE;
        }
2192
    }
2193
    iounit = get_iounit(pdu, &fidp->path);
2194
    stat_to_qid(&stbuf, &qid);
2195 2196 2197 2198 2199
    err = pdu_marshal(pdu, offset, "Qd", &qid, iounit);
    if (err < 0) {
        goto out;
    }
    err += offset;
2200 2201
    trace_v9fs_create_return(pdu->tag, pdu->id,
                             qid.type, qid.version, qid.path, iounit);
2202
out:
2203
    put_fid(pdu, fidp);
2204
out_nofid:
2205
   pdu_complete(pdu, err);
2206 2207
   v9fs_string_free(&name);
   v9fs_string_free(&extension);
2208
   v9fs_path_free(&path);
2209 2210
}

2211
static void v9fs_symlink(void *opaque)
2212
{
2213
    V9fsPDU *pdu = opaque;
2214 2215 2216 2217 2218
    V9fsString name;
    V9fsString symname;
    V9fsFidState *dfidp;
    V9fsQID qid;
    struct stat stbuf;
2219 2220 2221
    int32_t dfid;
    int err = 0;
    gid_t gid;
2222
    size_t offset = 7;
2223

2224 2225 2226 2227 2228 2229
    v9fs_string_init(&name);
    v9fs_string_init(&symname);
    err = pdu_unmarshal(pdu, offset, "dssd", &dfid, &name, &symname, &gid);
    if (err < 0) {
        goto out_nofid;
    }
2230
    trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid);
2231

2232
    dfidp = get_fid(pdu, dfid);
2233
    if (dfidp == NULL) {
2234
        err = -EINVAL;
2235
        goto out_nofid;
2236
    }
2237
    err = v9fs_co_symlink(pdu, dfidp, &name, symname.data, gid, &stbuf);
2238 2239 2240 2241
    if (err < 0) {
        goto out;
    }
    stat_to_qid(&stbuf, &qid);
2242 2243 2244 2245 2246
    err =  pdu_marshal(pdu, offset, "Q", &qid);
    if (err < 0) {
        goto out;
    }
    err += offset;
2247 2248
    trace_v9fs_symlink_return(pdu->tag, pdu->id,
                              qid.type, qid.version, qid.path);
2249
out:
2250
    put_fid(pdu, dfidp);
2251
out_nofid:
2252
    pdu_complete(pdu, err);
2253 2254
    v9fs_string_free(&name);
    v9fs_string_free(&symname);
2255 2256
}

2257
static void v9fs_flush(void *opaque)
2258
{
2259
    ssize_t err;
2260 2261 2262
    int16_t tag;
    size_t offset = 7;
    V9fsPDU *cancel_pdu;
2263 2264
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
2265

2266 2267
    err = pdu_unmarshal(pdu, offset, "w", &tag);
    if (err < 0) {
2268
        pdu_complete(pdu, err);
2269 2270
        return;
    }
2271
    trace_v9fs_flush(pdu->tag, pdu->id, tag);
2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284

    QLIST_FOREACH(cancel_pdu, &s->active_list, next) {
        if (cancel_pdu->tag == tag) {
            break;
        }
    }
    if (cancel_pdu) {
        cancel_pdu->cancelled = 1;
        /*
         * Wait for pdu to complete.
         */
        qemu_co_queue_wait(&cancel_pdu->complete);
        cancel_pdu->cancelled = 0;
2285
        pdu_free(cancel_pdu);
2286
    }
2287
    pdu_complete(pdu, 7);
2288 2289
}

2290
static void v9fs_link(void *opaque)
2291
{
2292
    V9fsPDU *pdu = opaque;
2293 2294
    int32_t dfid, oldfid;
    V9fsFidState *dfidp, *oldfidp;
2295
    V9fsString name;
2296 2297 2298
    size_t offset = 7;
    int err = 0;

2299 2300 2301 2302 2303
    v9fs_string_init(&name);
    err = pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
    if (err < 0) {
        goto out_nofid;
    }
2304
    trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data);
2305

2306
    dfidp = get_fid(pdu, dfid);
2307
    if (dfidp == NULL) {
2308
        err = -ENOENT;
2309
        goto out_nofid;
2310 2311
    }

2312
    oldfidp = get_fid(pdu, oldfid);
2313
    if (oldfidp == NULL) {
2314
        err = -ENOENT;
2315 2316
        goto out;
    }
2317
    err = v9fs_co_link(pdu, oldfidp, dfidp, &name);
2318 2319
    if (!err) {
        err = offset;
2320 2321
    }
out:
2322
    put_fid(pdu, dfidp);
2323
out_nofid:
2324
    v9fs_string_free(&name);
2325
    pdu_complete(pdu, err);
2326 2327
}

2328
/* Only works with path name based fid */
2329
static void v9fs_remove(void *opaque)
2330
{
2331 2332
    int32_t fid;
    int err = 0;
2333 2334 2335
    size_t offset = 7;
    V9fsFidState *fidp;
    V9fsPDU *pdu = opaque;
2336

2337 2338 2339 2340
    err = pdu_unmarshal(pdu, offset, "d", &fid);
    if (err < 0) {
        goto out_nofid;
    }
2341
    trace_v9fs_remove(pdu->tag, pdu->id, fid);
2342

2343
    fidp = get_fid(pdu, fid);
2344
    if (fidp == NULL) {
2345
        err = -EINVAL;
2346
        goto out_nofid;
2347
    }
2348
    /* if fs driver is not path based, return EOPNOTSUPP */
2349
    if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
2350 2351 2352
        err = -EOPNOTSUPP;
        goto out_err;
    }
2353 2354 2355 2356
    /*
     * IF the file is unlinked, we cannot reopen
     * the file later. So don't reclaim fd
     */
2357
    err = v9fs_mark_fids_unreclaim(pdu, &fidp->path);
2358 2359 2360
    if (err < 0) {
        goto out_err;
    }
2361
    err = v9fs_co_remove(pdu, &fidp->path);
2362 2363 2364
    if (!err) {
        err = offset;
    }
2365
out_err:
2366
    /* For TREMOVE we need to clunk the fid even on failed remove */
2367
    clunk_fid(pdu->s, fidp->fid);
2368
    put_fid(pdu, fidp);
2369
out_nofid:
2370
    pdu_complete(pdu, err);
2371 2372
}

2373 2374 2375 2376 2377 2378
static void v9fs_unlinkat(void *opaque)
{
    int err = 0;
    V9fsString name;
    int32_t dfid, flags;
    size_t offset = 7;
2379
    V9fsPath path;
2380 2381 2382
    V9fsFidState *dfidp;
    V9fsPDU *pdu = opaque;

2383 2384 2385 2386 2387
    v9fs_string_init(&name);
    err = pdu_unmarshal(pdu, offset, "dsd", &dfid, &name, &flags);
    if (err < 0) {
        goto out_nofid;
    }
2388
    dfidp = get_fid(pdu, dfid);
2389 2390 2391 2392 2393 2394 2395 2396
    if (dfidp == NULL) {
        err = -EINVAL;
        goto out_nofid;
    }
    /*
     * IF the file is unlinked, we cannot reopen
     * the file later. So don't reclaim fd
     */
2397
    v9fs_path_init(&path);
2398
    err = v9fs_co_name_to_path(pdu, &dfidp->path, name.data, &path);
2399 2400 2401
    if (err < 0) {
        goto out_err;
    }
2402
    err = v9fs_mark_fids_unreclaim(pdu, &path);
2403 2404 2405
    if (err < 0) {
        goto out_err;
    }
2406
    err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, flags);
2407 2408 2409 2410
    if (!err) {
        err = offset;
    }
out_err:
2411
    put_fid(pdu, dfidp);
2412
    v9fs_path_free(&path);
2413
out_nofid:
2414
    pdu_complete(pdu, err);
2415 2416 2417
    v9fs_string_free(&name);
}

2418 2419

/* Only works with path name based fid */
2420
static int v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp,
2421
                                int32_t newdirfid, V9fsString *name)
2422
{
2423
    char *end;
2424
    int err = 0;
2425 2426
    V9fsPath new_path;
    V9fsFidState *tfidp;
2427
    V9fsState *s = pdu->s;
2428
    V9fsFidState *dirfidp = NULL;
2429
    char *old_name, *new_name;
2430

2431
    v9fs_path_init(&new_path);
2432
    if (newdirfid != -1) {
2433
        dirfidp = get_fid(pdu, newdirfid);
2434 2435
        if (dirfidp == NULL) {
            err = -ENOENT;
2436
            goto out_nofid;
2437
        }
2438
        BUG_ON(dirfidp->fid_type != P9_FID_NONE);
2439
        v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path);
2440
    } else {
2441
        old_name = fidp->path.data;
2442 2443 2444 2445 2446 2447
        end = strrchr(old_name, '/');
        if (end) {
            end++;
        } else {
            end = old_name;
        }
2448
        new_name = g_malloc0(end - old_name + name->size + 1);
2449
        strncat(new_name, old_name, end - old_name);
2450
        strncat(new_name + (end - old_name), name->data, name->size);
2451
        v9fs_co_name_to_path(pdu, NULL, new_name, &new_path);
2452
        g_free(new_name);
2453
    }
2454
    err = v9fs_co_rename(pdu, &fidp->path, &new_path);
2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465
    if (err < 0) {
        goto out;
    }
    /*
     * Fixup fid's pointing to the old name to
     * start pointing to the new name
     */
    for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) {
        if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) {
            /* replace the name */
            v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data));
2466 2467
        }
    }
2468
out:
2469
    if (dirfidp) {
2470
        put_fid(pdu, dirfidp);
2471
    }
2472
    v9fs_path_free(&new_path);
2473
out_nofid:
2474 2475 2476
    return err;
}

2477
/* Only works with path name based fid */
2478
static void v9fs_rename(void *opaque)
2479 2480 2481
{
    int32_t fid;
    ssize_t err = 0;
2482 2483 2484 2485 2486 2487
    size_t offset = 7;
    V9fsString name;
    int32_t newdirfid;
    V9fsFidState *fidp;
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
2488

2489 2490 2491 2492 2493
    v9fs_string_init(&name);
    err = pdu_unmarshal(pdu, offset, "dds", &fid, &newdirfid, &name);
    if (err < 0) {
        goto out_nofid;
    }
2494
    fidp = get_fid(pdu, fid);
2495
    if (fidp == NULL) {
2496
        err = -ENOENT;
2497
        goto out_nofid;
2498
    }
2499
    BUG_ON(fidp->fid_type != P9_FID_NONE);
2500
    /* if fs driver is not path based, return EOPNOTSUPP */
2501
    if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
2502 2503 2504 2505
        err = -EOPNOTSUPP;
        goto out;
    }
    v9fs_path_write_lock(s);
2506
    err = v9fs_complete_rename(pdu, fidp, newdirfid, &name);
2507
    v9fs_path_unlock(s);
2508 2509 2510
    if (!err) {
        err = offset;
    }
2511
out:
2512
    put_fid(pdu, fidp);
2513
out_nofid:
2514
    pdu_complete(pdu, err);
2515
    v9fs_string_free(&name);
2516 2517
}

2518
static void v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
2519 2520 2521 2522 2523
                               V9fsString *old_name, V9fsPath *newdir,
                               V9fsString *new_name)
{
    V9fsFidState *tfidp;
    V9fsPath oldpath, newpath;
2524
    V9fsState *s = pdu->s;
2525 2526 2527 2528


    v9fs_path_init(&oldpath);
    v9fs_path_init(&newpath);
2529 2530
    v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath);
    v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath);
2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545

    /*
     * Fixup fid's pointing to the old name to
     * start pointing to the new name
     */
    for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) {
        if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) {
            /* replace the name */
            v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data));
        }
    }
    v9fs_path_free(&oldpath);
    v9fs_path_free(&newpath);
}

2546
static int v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid,
2547 2548 2549 2550
                                  V9fsString *old_name, int32_t newdirfid,
                                  V9fsString *new_name)
{
    int err = 0;
2551
    V9fsState *s = pdu->s;
2552 2553
    V9fsFidState *newdirfidp = NULL, *olddirfidp = NULL;

2554
    olddirfidp = get_fid(pdu, olddirfid);
2555 2556 2557 2558 2559
    if (olddirfidp == NULL) {
        err = -ENOENT;
        goto out;
    }
    if (newdirfid != -1) {
2560
        newdirfidp = get_fid(pdu, newdirfid);
2561 2562 2563 2564 2565
        if (newdirfidp == NULL) {
            err = -ENOENT;
            goto out;
        }
    } else {
2566
        newdirfidp = get_fid(pdu, olddirfid);
2567 2568
    }

2569
    err = v9fs_co_renameat(pdu, &olddirfidp->path, old_name,
2570 2571 2572
                           &newdirfidp->path, new_name);
    if (err < 0) {
        goto out;
2573
    }
2574
    if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
2575
        /* Only for path based fid  we need to do the below fixup */
2576
        v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name,
2577 2578
                           &newdirfidp->path, new_name);
    }
2579 2580
out:
    if (olddirfidp) {
2581
        put_fid(pdu, olddirfidp);
2582 2583
    }
    if (newdirfidp) {
2584
        put_fid(pdu, newdirfidp);
2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597
    }
    return err;
}

static void v9fs_renameat(void *opaque)
{
    ssize_t err = 0;
    size_t offset = 7;
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
    int32_t olddirfid, newdirfid;
    V9fsString old_name, new_name;

2598 2599 2600 2601 2602 2603 2604
    v9fs_string_init(&old_name);
    v9fs_string_init(&new_name);
    err = pdu_unmarshal(pdu, offset, "dsds", &olddirfid,
                        &old_name, &newdirfid, &new_name);
    if (err < 0) {
        goto out_err;
    }
2605

2606
    v9fs_path_write_lock(s);
2607 2608
    err = v9fs_complete_renameat(pdu, olddirfid,
                                 &old_name, newdirfid, &new_name);
2609
    v9fs_path_unlock(s);
2610 2611 2612
    if (!err) {
        err = offset;
    }
2613 2614

out_err:
2615
    pdu_complete(pdu, err);
2616 2617 2618 2619
    v9fs_string_free(&old_name);
    v9fs_string_free(&new_name);
}

2620
static void v9fs_wstat(void *opaque)
2621
{
2622 2623 2624 2625 2626 2627 2628 2629
    int32_t fid;
    int err = 0;
    int16_t unused;
    V9fsStat v9stat;
    size_t offset = 7;
    struct stat stbuf;
    V9fsFidState *fidp;
    V9fsPDU *pdu = opaque;
2630

2631 2632 2633 2634 2635
    v9fs_stat_init(&v9stat);
    err = pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat);
    if (err < 0) {
        goto out_nofid;
    }
2636 2637
    trace_v9fs_wstat(pdu->tag, pdu->id, fid,
                     v9stat.mode, v9stat.atime, v9stat.mtime);
2638

2639
    fidp = get_fid(pdu, fid);
2640 2641
    if (fidp == NULL) {
        err = -EINVAL;
2642
        goto out_nofid;
2643
    }
2644 2645
    /* do we need to sync the file? */
    if (donttouch_stat(&v9stat)) {
2646
        err = v9fs_co_fsync(pdu, fidp, 0);
2647 2648
        goto out;
    }
2649 2650
    if (v9stat.mode != -1) {
        uint32_t v9_mode;
2651
        err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
2652 2653 2654 2655 2656 2657 2658 2659 2660 2661
        if (err < 0) {
            goto out;
        }
        v9_mode = stat_to_v9mode(&stbuf);
        if ((v9stat.mode & P9_STAT_MODE_TYPE_BITS) !=
            (v9_mode & P9_STAT_MODE_TYPE_BITS)) {
            /* Attempting to change the type */
            err = -EIO;
            goto out;
        }
2662
        err = v9fs_co_chmod(pdu, &fidp->path,
2663 2664 2665 2666 2667 2668 2669
                            v9mode_to_mode(v9stat.mode,
                                           &v9stat.extension));
        if (err < 0) {
            goto out;
        }
    }
    if (v9stat.mtime != -1 || v9stat.atime != -1) {
2670
        struct timespec times[2];
2671 2672
        if (v9stat.atime != -1) {
            times[0].tv_sec = v9stat.atime;
2673 2674 2675 2676
            times[0].tv_nsec = 0;
        } else {
            times[0].tv_nsec = UTIME_OMIT;
        }
2677 2678
        if (v9stat.mtime != -1) {
            times[1].tv_sec = v9stat.mtime;
2679 2680 2681 2682
            times[1].tv_nsec = 0;
        } else {
            times[1].tv_nsec = UTIME_OMIT;
        }
2683
        err = v9fs_co_utimensat(pdu, &fidp->path, times);
2684 2685
        if (err < 0) {
            goto out;
2686 2687
        }
    }
2688
    if (v9stat.n_gid != -1 || v9stat.n_uid != -1) {
2689
        err = v9fs_co_chown(pdu, &fidp->path, v9stat.n_uid, v9stat.n_gid);
2690
        if (err < 0) {
2691
            goto out;
2692
        }
2693
    }
2694
    if (v9stat.name.size != 0) {
2695
        err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name);
2696 2697 2698
        if (err < 0) {
            goto out;
        }
2699
    }
2700
    if (v9stat.length != -1) {
2701
        err = v9fs_co_truncate(pdu, &fidp->path, v9stat.length);
2702 2703 2704
        if (err < 0) {
            goto out;
        }
2705
    }
2706
    err = offset;
2707
out:
2708
    put_fid(pdu, fidp);
2709
out_nofid:
2710
    v9fs_stat_free(&v9stat);
2711
    pdu_complete(pdu, err);
2712 2713
}

2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725
static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf)
{
    uint32_t f_type;
    uint32_t f_bsize;
    uint64_t f_blocks;
    uint64_t f_bfree;
    uint64_t f_bavail;
    uint64_t f_files;
    uint64_t f_ffree;
    uint64_t fsid_val;
    uint32_t f_namelen;
    size_t offset = 7;
2726 2727 2728 2729 2730 2731
    int32_t bsize_factor;

    /*
     * compute bsize factor based on host file system block size
     * and client msize
     */
2732
    bsize_factor = (s->msize - P9_IOHDRSZ)/stbuf->f_bsize;
2733 2734 2735
    if (!bsize_factor) {
        bsize_factor = 1;
    }
2736 2737 2738
    f_type  = stbuf->f_type;
    f_bsize = stbuf->f_bsize;
    f_bsize *= bsize_factor;
2739 2740 2741 2742 2743
    /*
     * f_bsize is adjusted(multiplied) by bsize factor, so we need to
     * adjust(divide) the number of blocks, free blocks and available
     * blocks by bsize factor
     */
2744 2745 2746 2747 2748 2749 2750 2751
    f_blocks = stbuf->f_blocks/bsize_factor;
    f_bfree  = stbuf->f_bfree/bsize_factor;
    f_bavail = stbuf->f_bavail/bsize_factor;
    f_files  = stbuf->f_files;
    f_ffree  = stbuf->f_ffree;
    fsid_val = (unsigned int) stbuf->f_fsid.__val[0] |
               (unsigned long long)stbuf->f_fsid.__val[1] << 32;
    f_namelen = stbuf->f_namelen;
2752

2753 2754 2755 2756
    return pdu_marshal(pdu, offset, "ddqqqqqqd",
                       f_type, f_bsize, f_blocks, f_bfree,
                       f_bavail, f_files, f_ffree,
                       fsid_val, f_namelen);
2757 2758
}

2759
static void v9fs_statfs(void *opaque)
2760
{
2761 2762 2763 2764 2765
    int32_t fid;
    ssize_t retval = 0;
    size_t offset = 7;
    V9fsFidState *fidp;
    struct statfs stbuf;
2766 2767
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
2768

2769 2770 2771 2772
    retval = pdu_unmarshal(pdu, offset, "d", &fid);
    if (retval < 0) {
        goto out_nofid;
    }
2773
    fidp = get_fid(pdu, fid);
2774 2775
    if (fidp == NULL) {
        retval = -ENOENT;
2776
        goto out_nofid;
2777
    }
2778
    retval = v9fs_co_statfs(pdu, &fidp->path, &stbuf);
2779 2780 2781
    if (retval < 0) {
        goto out;
    }
2782 2783 2784 2785 2786
    retval = v9fs_fill_statfs(s, pdu, &stbuf);
    if (retval < 0) {
        goto out;
    }
    retval += offset;
2787
out:
2788
    put_fid(pdu, fidp);
2789
out_nofid:
2790
    pdu_complete(pdu, retval);
2791 2792
}

2793
static void v9fs_mknod(void *opaque)
2794
{
2795 2796 2797

    int mode;
    gid_t gid;
2798
    int32_t fid;
2799
    V9fsQID qid;
2800 2801
    int err = 0;
    int major, minor;
2802 2803 2804 2805 2806
    size_t offset = 7;
    V9fsString name;
    struct stat stbuf;
    V9fsFidState *fidp;
    V9fsPDU *pdu = opaque;
2807

2808 2809 2810 2811 2812 2813
    v9fs_string_init(&name);
    err = pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode,
                        &major, &minor, &gid);
    if (err < 0) {
        goto out_nofid;
    }
2814
    trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor);
2815

2816
    fidp = get_fid(pdu, fid);
2817 2818
    if (fidp == NULL) {
        err = -ENOENT;
2819
        goto out_nofid;
2820
    }
2821
    err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, gid,
2822
                        makedev(major, minor), mode, &stbuf);
2823 2824 2825 2826
    if (err < 0) {
        goto out;
    }
    stat_to_qid(&stbuf, &qid);
2827 2828 2829 2830 2831
    err = pdu_marshal(pdu, offset, "Q", &qid);
    if (err < 0) {
        goto out;
    }
    err += offset;
2832 2833
    trace_v9fs_mknod_return(pdu->tag, pdu->id,
                            qid.type, qid.version, qid.path);
2834
out:
2835
    put_fid(pdu, fidp);
2836
out_nofid:
2837
    pdu_complete(pdu, err);
2838
    v9fs_string_free(&name);
2839 2840
}

M
M. Mohan Kumar 已提交
2841 2842 2843 2844 2845 2846 2847 2848
/*
 * Implement posix byte range locking code
 * Server side handling of locking code is very simple, because 9p server in
 * QEMU can handle only one client. And most of the lock handling
 * (like conflict, merging) etc is done by the VFS layer itself, so no need to
 * do any thing in * qemu 9p server side lock code path.
 * So when a TLOCK request comes, always return success
 */
2849
static void v9fs_lock(void *opaque)
M
M. Mohan Kumar 已提交
2850
{
2851
    int8_t status;
2852
    V9fsFlock flock;
2853 2854 2855 2856
    size_t offset = 7;
    struct stat stbuf;
    V9fsFidState *fidp;
    int32_t fid, err = 0;
2857
    V9fsPDU *pdu = opaque;
M
M. Mohan Kumar 已提交
2858

2859 2860 2861 2862 2863 2864 2865 2866
    status = P9_LOCK_ERROR;
    v9fs_string_init(&flock.client_id);
    err = pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock.type,
                        &flock.flags, &flock.start, &flock.length,
                        &flock.proc_id, &flock.client_id);
    if (err < 0) {
        goto out_nofid;
    }
2867
    trace_v9fs_lock(pdu->tag, pdu->id, fid,
2868
                    flock.type, flock.start, flock.length);
2869

M
M. Mohan Kumar 已提交
2870 2871

    /* We support only block flag now (that too ignored currently) */
2872
    if (flock.flags & ~P9_LOCK_FLAGS_BLOCK) {
M
M. Mohan Kumar 已提交
2873
        err = -EINVAL;
2874
        goto out_nofid;
M
M. Mohan Kumar 已提交
2875
    }
2876
    fidp = get_fid(pdu, fid);
2877
    if (fidp == NULL) {
M
M. Mohan Kumar 已提交
2878
        err = -ENOENT;
2879
        goto out_nofid;
M
M. Mohan Kumar 已提交
2880
    }
2881
    err = v9fs_co_fstat(pdu, fidp, &stbuf);
M
M. Mohan Kumar 已提交
2882 2883 2884
    if (err < 0) {
        goto out;
    }
2885
    status = P9_LOCK_SUCCESS;
M
M. Mohan Kumar 已提交
2886
out:
2887
    put_fid(pdu, fidp);
2888
out_nofid:
2889 2890 2891 2892
    err = pdu_marshal(pdu, offset, "b", status);
    if (err > 0) {
        err += offset;
    }
2893
    trace_v9fs_lock_return(pdu->tag, pdu->id, status);
2894
    pdu_complete(pdu, err);
2895
    v9fs_string_free(&flock.client_id);
M
M. Mohan Kumar 已提交
2896 2897
}

M
M. Mohan Kumar 已提交
2898 2899 2900 2901
/*
 * When a TGETLOCK request comes, always return success because all lock
 * handling is done by client's VFS layer.
 */
2902
static void v9fs_getlock(void *opaque)
M
M. Mohan Kumar 已提交
2903
{
2904 2905 2906
    size_t offset = 7;
    struct stat stbuf;
    V9fsFidState *fidp;
2907
    V9fsGetlock glock;
2908
    int32_t fid, err = 0;
2909
    V9fsPDU *pdu = opaque;
M
M. Mohan Kumar 已提交
2910

2911 2912 2913 2914 2915 2916 2917
    v9fs_string_init(&glock.client_id);
    err = pdu_unmarshal(pdu, offset, "dbqqds", &fid, &glock.type,
                        &glock.start, &glock.length, &glock.proc_id,
                        &glock.client_id);
    if (err < 0) {
        goto out_nofid;
    }
2918
    trace_v9fs_getlock(pdu->tag, pdu->id, fid,
2919
                       glock.type, glock.start, glock.length);
2920

2921
    fidp = get_fid(pdu, fid);
2922
    if (fidp == NULL) {
M
M. Mohan Kumar 已提交
2923
        err = -ENOENT;
2924
        goto out_nofid;
M
M. Mohan Kumar 已提交
2925
    }
2926
    err = v9fs_co_fstat(pdu, fidp, &stbuf);
M
M. Mohan Kumar 已提交
2927 2928 2929
    if (err < 0) {
        goto out;
    }
2930 2931 2932 2933 2934 2935 2936 2937 2938 2939
    glock.type = P9_LOCK_TYPE_UNLCK;
    err = pdu_marshal(pdu, offset, "bqqds", glock.type,
                          glock.start, glock.length, glock.proc_id,
                          &glock.client_id);
    if (err < 0) {
        goto out;
    }
    err += offset;
    trace_v9fs_getlock_return(pdu->tag, pdu->id, glock.type, glock.start,
                              glock.length, glock.proc_id);
M
M. Mohan Kumar 已提交
2940
out:
2941
    put_fid(pdu, fidp);
2942
out_nofid:
2943
    pdu_complete(pdu, err);
2944
    v9fs_string_free(&glock.client_id);
M
M. Mohan Kumar 已提交
2945 2946
}

2947
static void v9fs_mkdir(void *opaque)
2948
{
2949
    V9fsPDU *pdu = opaque;
2950
    size_t offset = 7;
2951
    int32_t fid;
2952 2953
    struct stat stbuf;
    V9fsQID qid;
2954
    V9fsString name;
2955 2956 2957
    V9fsFidState *fidp;
    gid_t gid;
    int mode;
2958
    int err = 0;
2959

2960 2961 2962 2963 2964
    v9fs_string_init(&name);
    err = pdu_unmarshal(pdu, offset, "dsdd", &fid, &name, &mode, &gid);
    if (err < 0) {
        goto out_nofid;
    }
2965 2966
    trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid);

2967
    fidp = get_fid(pdu, fid);
2968 2969
    if (fidp == NULL) {
        err = -ENOENT;
2970
        goto out_nofid;
2971
    }
2972
    err = v9fs_co_mkdir(pdu, fidp, &name, mode, fidp->uid, gid, &stbuf);
2973 2974 2975 2976
    if (err < 0) {
        goto out;
    }
    stat_to_qid(&stbuf, &qid);
2977 2978 2979 2980 2981
    err = pdu_marshal(pdu, offset, "Q", &qid);
    if (err < 0) {
        goto out;
    }
    err += offset;
2982 2983
    trace_v9fs_mkdir_return(pdu->tag, pdu->id,
                            qid.type, qid.version, qid.path, err);
2984
out:
2985
    put_fid(pdu, fidp);
2986
out_nofid:
2987
    pdu_complete(pdu, err);
2988
    v9fs_string_free(&name);
2989 2990
}

2991
static void v9fs_xattrwalk(void *opaque)
2992
{
2993 2994
    int64_t size;
    V9fsString name;
2995
    ssize_t err = 0;
2996
    size_t offset = 7;
2997
    int32_t fid, newfid;
2998
    V9fsFidState *file_fidp;
2999
    V9fsFidState *xattr_fidp = NULL;
3000 3001
    V9fsPDU *pdu = opaque;
    V9fsState *s = pdu->s;
3002

3003 3004 3005 3006 3007
    v9fs_string_init(&name);
    err = pdu_unmarshal(pdu, offset, "dds", &fid, &newfid, &name);
    if (err < 0) {
        goto out_nofid;
    }
3008 3009
    trace_v9fs_xattrwalk(pdu->tag, pdu->id, fid, newfid, name.data);

3010
    file_fidp = get_fid(pdu, fid);
3011
    if (file_fidp == NULL) {
3012
        err = -ENOENT;
3013
        goto out_nofid;
3014
    }
3015 3016
    xattr_fidp = alloc_fid(s, newfid);
    if (xattr_fidp == NULL) {
3017 3018 3019
        err = -EINVAL;
        goto out;
    }
3020
    v9fs_path_copy(&xattr_fidp->path, &file_fidp->path);
3021
    if (name.data == NULL) {
3022 3023 3024
        /*
         * listxattr request. Get the size first
         */
3025
        size = v9fs_co_llistxattr(pdu, &xattr_fidp->path, NULL, 0);
3026 3027
        if (size < 0) {
            err = size;
3028
            clunk_fid(s, xattr_fidp->fid);
3029
            goto out;
3030
        }
3031 3032 3033 3034 3035 3036 3037
        /*
         * Read the xattr value
         */
        xattr_fidp->fs.xattr.len = size;
        xattr_fidp->fid_type = P9_FID_XATTR;
        xattr_fidp->fs.xattr.copied_len = -1;
        if (size) {
3038
            xattr_fidp->fs.xattr.value = g_malloc(size);
3039
            err = v9fs_co_llistxattr(pdu, &xattr_fidp->path,
3040 3041 3042
                                     xattr_fidp->fs.xattr.value,
                                     xattr_fidp->fs.xattr.len);
            if (err < 0) {
3043
                clunk_fid(s, xattr_fidp->fid);
3044 3045 3046
                goto out;
            }
        }
3047 3048 3049 3050 3051
        err = pdu_marshal(pdu, offset, "q", size);
        if (err < 0) {
            goto out;
        }
        err += offset;
3052 3053 3054 3055 3056
    } else {
        /*
         * specific xattr fid. We check for xattr
         * presence also collect the xattr size
         */
3057
        size = v9fs_co_lgetxattr(pdu, &xattr_fidp->path,
3058 3059 3060
                                 &name, NULL, 0);
        if (size < 0) {
            err = size;
3061
            clunk_fid(s, xattr_fidp->fid);
3062
            goto out;
3063
        }
3064 3065 3066 3067 3068 3069 3070
        /*
         * Read the xattr value
         */
        xattr_fidp->fs.xattr.len = size;
        xattr_fidp->fid_type = P9_FID_XATTR;
        xattr_fidp->fs.xattr.copied_len = -1;
        if (size) {
3071
            xattr_fidp->fs.xattr.value = g_malloc(size);
3072
            err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path,
3073 3074 3075
                                    &name, xattr_fidp->fs.xattr.value,
                                    xattr_fidp->fs.xattr.len);
            if (err < 0) {
3076
                clunk_fid(s, xattr_fidp->fid);
3077 3078 3079
                goto out;
            }
        }
3080 3081 3082 3083 3084
        err = pdu_marshal(pdu, offset, "q", size);
        if (err < 0) {
            goto out;
        }
        err += offset;
3085
    }
3086
    trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size);
3087
out:
3088
    put_fid(pdu, file_fidp);
3089
    if (xattr_fidp) {
3090
        put_fid(pdu, xattr_fidp);
3091 3092
    }
out_nofid:
3093
    pdu_complete(pdu, err);
3094
    v9fs_string_free(&name);
3095 3096
}

3097
static void v9fs_xattrcreate(void *opaque)
3098 3099 3100
{
    int flags;
    int32_t fid;
3101
    int64_t size;
3102
    ssize_t err = 0;
3103 3104 3105 3106 3107
    V9fsString name;
    size_t offset = 7;
    V9fsFidState *file_fidp;
    V9fsFidState *xattr_fidp;
    V9fsPDU *pdu = opaque;
3108

3109 3110 3111 3112 3113
    v9fs_string_init(&name);
    err = pdu_unmarshal(pdu, offset, "dsqd", &fid, &name, &size, &flags);
    if (err < 0) {
        goto out_nofid;
    }
3114
    trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags);
3115

3116
    file_fidp = get_fid(pdu, fid);
3117
    if (file_fidp == NULL) {
3118
        err = -EINVAL;
3119
        goto out_nofid;
3120 3121
    }
    /* Make the file fid point to xattr */
3122 3123 3124 3125 3126 3127 3128
    xattr_fidp = file_fidp;
    xattr_fidp->fid_type = P9_FID_XATTR;
    xattr_fidp->fs.xattr.copied_len = 0;
    xattr_fidp->fs.xattr.len = size;
    xattr_fidp->fs.xattr.flags = flags;
    v9fs_string_init(&xattr_fidp->fs.xattr.name);
    v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name);
3129
    xattr_fidp->fs.xattr.value = g_malloc(size);
3130
    err = offset;
3131
    put_fid(pdu, file_fidp);
3132
out_nofid:
3133
    pdu_complete(pdu, err);
3134
    v9fs_string_free(&name);
3135
}
3136

3137
static void v9fs_readlink(void *opaque)
3138
{
3139
    V9fsPDU *pdu = opaque;
3140 3141
    size_t offset = 7;
    V9fsString target;
3142 3143 3144 3145
    int32_t fid;
    int err = 0;
    V9fsFidState *fidp;

3146 3147 3148 3149
    err = pdu_unmarshal(pdu, offset, "d", &fid);
    if (err < 0) {
        goto out_nofid;
    }
3150
    trace_v9fs_readlink(pdu->tag, pdu->id, fid);
3151
    fidp = get_fid(pdu, fid);
3152 3153
    if (fidp == NULL) {
        err = -ENOENT;
3154
        goto out_nofid;
3155 3156
    }

3157
    v9fs_string_init(&target);
3158
    err = v9fs_co_readlink(pdu, &fidp->path, &target);
3159 3160 3161
    if (err < 0) {
        goto out;
    }
3162 3163 3164 3165 3166 3167
    err = pdu_marshal(pdu, offset, "s", &target);
    if (err < 0) {
        v9fs_string_free(&target);
        goto out;
    }
    err += offset;
3168
    trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data);
3169
    v9fs_string_free(&target);
3170
out:
3171
    put_fid(pdu, fidp);
3172
out_nofid:
3173
    pdu_complete(pdu, err);
3174 3175
}

3176
static CoroutineEntry *pdu_co_handlers[] = {
3177
    [P9_TREADDIR] = v9fs_readdir,
3178
    [P9_TSTATFS] = v9fs_statfs,
3179
    [P9_TGETATTR] = v9fs_getattr,
3180
    [P9_TSETATTR] = v9fs_setattr,
3181
    [P9_TXATTRWALK] = v9fs_xattrwalk,
3182
    [P9_TXATTRCREATE] = v9fs_xattrcreate,
3183
    [P9_TMKNOD] = v9fs_mknod,
3184
    [P9_TRENAME] = v9fs_rename,
M
M. Mohan Kumar 已提交
3185
    [P9_TLOCK] = v9fs_lock,
M
M. Mohan Kumar 已提交
3186
    [P9_TGETLOCK] = v9fs_getlock,
3187
    [P9_TRENAMEAT] = v9fs_renameat,
3188
    [P9_TREADLINK] = v9fs_readlink,
3189
    [P9_TUNLINKAT] = v9fs_unlinkat,
3190
    [P9_TMKDIR] = v9fs_mkdir,
3191
    [P9_TVERSION] = v9fs_version,
3192
    [P9_TLOPEN] = v9fs_open,
3193 3194 3195 3196
    [P9_TATTACH] = v9fs_attach,
    [P9_TSTAT] = v9fs_stat,
    [P9_TWALK] = v9fs_walk,
    [P9_TCLUNK] = v9fs_clunk,
3197
    [P9_TFSYNC] = v9fs_fsync,
3198 3199 3200 3201 3202 3203
    [P9_TOPEN] = v9fs_open,
    [P9_TREAD] = v9fs_read,
#if 0
    [P9_TAUTH] = v9fs_auth,
#endif
    [P9_TFLUSH] = v9fs_flush,
3204
    [P9_TLINK] = v9fs_link,
3205
    [P9_TSYMLINK] = v9fs_symlink,
3206
    [P9_TCREATE] = v9fs_create,
3207
    [P9_TLCREATE] = v9fs_lcreate,
3208 3209 3210 3211 3212
    [P9_TWRITE] = v9fs_write,
    [P9_TWSTAT] = v9fs_wstat,
    [P9_TREMOVE] = v9fs_remove,
};

3213
static void v9fs_op_not_supp(void *opaque)
3214
{
3215
    V9fsPDU *pdu = opaque;
3216
    pdu_complete(pdu, -EOPNOTSUPP);
3217 3218
}

3219 3220 3221
static void v9fs_fs_ro(void *opaque)
{
    V9fsPDU *pdu = opaque;
3222
    pdu_complete(pdu, -EROFS);
3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251
}

static inline bool is_read_only_op(V9fsPDU *pdu)
{
    switch (pdu->id) {
    case P9_TREADDIR:
    case P9_TSTATFS:
    case P9_TGETATTR:
    case P9_TXATTRWALK:
    case P9_TLOCK:
    case P9_TGETLOCK:
    case P9_TREADLINK:
    case P9_TVERSION:
    case P9_TLOPEN:
    case P9_TATTACH:
    case P9_TSTAT:
    case P9_TWALK:
    case P9_TCLUNK:
    case P9_TFSYNC:
    case P9_TOPEN:
    case P9_TREAD:
    case P9_TAUTH:
    case P9_TFLUSH:
        return 1;
    default:
        return 0;
    }
}

W
Wei Liu 已提交
3252
void pdu_submit(V9fsPDU *pdu)
3253
{
3254 3255
    Coroutine *co;
    CoroutineEntry *handler;
3256
    V9fsState *s = pdu->s;
3257

3258 3259
    if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
        (pdu_co_handlers[pdu->id] == NULL)) {
3260 3261
        handler = v9fs_op_not_supp;
    } else {
3262
        handler = pdu_co_handlers[pdu->id];
3263
    }
3264 3265 3266 3267

    if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
        handler = v9fs_fs_ro;
    }
3268 3269
    co = qemu_coroutine_create(handler);
    qemu_coroutine_enter(co, pdu);
3270 3271
}

3272 3273 3274
/* Returns 0 on success, 1 on failure. */
int v9fs_device_realize_common(V9fsState *s, Error **errp)
{
W
Wei Liu 已提交
3275
    V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
3276 3277 3278 3279 3280 3281 3282 3283 3284 3285
    int i, len;
    struct stat stat;
    FsDriverEntry *fse;
    V9fsPath path;
    int rc = 1;

    /* initialize pdu allocator */
    QLIST_INIT(&s->free_list);
    QLIST_INIT(&s->active_list);
    for (i = 0; i < (MAX_REQ - 1); i++) {
W
Wei Liu 已提交
3286 3287 3288
        QLIST_INSERT_HEAD(&s->free_list, &v->pdus[i], next);
        v->pdus[i].s = s;
        v->pdus[i].idx = i;
3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368
    }

    v9fs_path_init(&path);

    fse = get_fsdev_fsentry(s->fsconf.fsdev_id);

    if (!fse) {
        /* We don't have a fsdev identified by fsdev_id */
        error_setg(errp, "9pfs device couldn't find fsdev with the "
                   "id = %s",
                   s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL");
        goto out;
    }

    if (!s->fsconf.tag) {
        /* we haven't specified a mount_tag */
        error_setg(errp, "fsdev with id %s needs mount_tag arguments",
                   s->fsconf.fsdev_id);
        goto out;
    }

    s->ctx.export_flags = fse->export_flags;
    s->ctx.fs_root = g_strdup(fse->path);
    s->ctx.exops.get_st_gen = NULL;
    len = strlen(s->fsconf.tag);
    if (len > MAX_TAG_LEN - 1) {
        error_setg(errp, "mount tag '%s' (%d bytes) is longer than "
                   "maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1);
        goto out;
    }

    s->tag = g_strdup(s->fsconf.tag);
    s->ctx.uid = -1;

    s->ops = fse->ops;

    s->fid_list = NULL;
    qemu_co_rwlock_init(&s->rename_lock);

    if (s->ops->init(&s->ctx) < 0) {
        error_setg(errp, "9pfs Failed to initialize fs-driver with id:%s"
                   " and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root);
        goto out;
    }

    /*
     * Check details of export path, We need to use fs driver
     * call back to do that. Since we are in the init path, we don't
     * use co-routines here.
     */
    if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) {
        error_setg(errp,
                   "error in converting name to path %s", strerror(errno));
        goto out;
    }
    if (s->ops->lstat(&s->ctx, &path, &stat)) {
        error_setg(errp, "share path %s does not exist", fse->path);
        goto out;
    } else if (!S_ISDIR(stat.st_mode)) {
        error_setg(errp, "share path %s is not a directory", fse->path);
        goto out;
    }
    v9fs_path_free(&path);

    rc = 0;
out:
    if (rc) {
        g_free(s->ctx.fs_root);
        g_free(s->tag);
        v9fs_path_free(&path);
    }
    return rc;
}

void v9fs_device_unrealize_common(V9fsState *s, Error **errp)
{
    g_free(s->ctx.fs_root);
    g_free(s->tag);
}

3369
static void __attribute__((__constructor__)) v9fs_set_fd_limit(void)
3370 3371 3372 3373 3374 3375 3376 3377 3378
{
    struct rlimit rlim;
    if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
        fprintf(stderr, "Failed to get the resource limit\n");
        exit(1);
    }
    open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur/3);
    open_fd_rc = rlim.rlim_cur/2;
}