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

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

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

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

    va_start(ap, fmt);
49
    ret = virtio_pdu_vmarshal(pdu, offset, fmt, ap);
50 51 52 53 54 55 56 57 58 59 60
    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);
61
    ret = virtio_pdu_vunmarshal(pdu, offset, fmt, ap);
62 63 64 65 66
    va_end(ap);

    return ret;
}

W
Wei Liu 已提交
67 68
static void pdu_push_and_notify(V9fsPDU *pdu)
{
69
    virtio_9p_push_and_notify(pdu);
W
Wei Liu 已提交
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 105
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 已提交
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 145
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;
}

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

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

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 200
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;
}

201 202 203 204 205 206
/*
 * 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!
 */
207
static int v9fs_path_is_ancestor(V9fsPath *s1, V9fsPath *s2)
208
{
209 210
    if (!strncmp(s1->data, s2->data, s1->size - 1)) {
        if (s2->data[s1->size - 1] == '\0' || s2->data[s1->size - 1] == '/') {
211 212 213 214 215 216
            return 1;
        }
    }
    return 0;
}

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

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

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

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

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

284 285 286 287 288 289
    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;
        }
290
    }
291
    f = g_malloc0(sizeof(V9fsFidState));
292
    f->fid = fid;
293
    f->fid_type = P9_FID_NONE;
294
    f->ref = 1;
295 296 297 298 299
    /*
     * Mark the fid as referenced so that the LRU
     * reclaim won't close the file descriptor
     */
    f->flags |= FID_REFERENCED;
300 301 302 303 304 305
    f->next = s->fid_list;
    s->fid_list = f;

    return f;
}

306
static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp)
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
{
    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;
    }
323
    if (fidp->fs.xattr.len) {
324
        retval = v9fs_co_lsetxattr(pdu, &fidp->path, &fidp->fs.xattr.name,
325 326 327 328
                                   fidp->fs.xattr.value,
                                   fidp->fs.xattr.len,
                                   fidp->fs.xattr.flags);
    } else {
329
        retval = v9fs_co_lremovexattr(pdu, &fidp->path, &fidp->fs.xattr.name);
330
    }
331 332 333
free_out:
    v9fs_string_free(&fidp->fs.xattr.name);
free_value:
334
    g_free(fidp->fs.xattr.value);
335 336 337
    return retval;
}

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

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

359
static int put_fid(V9fsPDU *pdu, V9fsFidState *fidp)
360 361 362
{
    BUG_ON(!fidp->ref);
    fidp->ref--;
363 364 365
    /*
     * Don't free the fid if it is in reclaim list
     */
366
    if (!fidp->ref && fidp->clunked) {
367 368 369 370 371 372 373 374 375 376 377 378 379
        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;
            }
        }
380
        return free_fid(pdu, fidp);
381
    }
382
    return 0;
383 384
}

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

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

403
void v9fs_reclaim_fd(V9fsPDU *pdu)
404 405
{
    int reclaim_count = 0;
406
    V9fsState *s = pdu->s;
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 444
    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++;
            }
445 446 447 448 449 450 451 452 453 454 455 456 457
        } 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++;
            }
458 459 460 461 462 463 464 465 466 467 468 469 470
        }
        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) {
471
            v9fs_co_close(pdu, &f->fs_reclaim);
472
        } else if (f->fid_type == P9_FID_DIR) {
473
            v9fs_co_closedir(pdu, &f->fs_reclaim);
474 475 476 477 478 479
        }
        f->rclm_lst = NULL;
        /*
         * Now drop the fid reference, free it
         * if clunked.
         */
480
        put_fid(pdu, f);
481 482 483
    }
}

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

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

            /* reopen the file/dir if already closed */
500
            err = v9fs_reopen_fid(pdu, fidp);
501 502 503 504 505 506 507 508 509
            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) {
510 511 512 513 514 515 516
                fidp = &head_fid;
            }
        }
    }
    return 0;
}

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
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__);
    }
}

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 569
#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;

570
    memset(&qidp->path, 0, sizeof(qidp->path));
571 572 573 574 575 576 577 578 579 580 581 582
    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;
    }
}

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

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

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

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

W
Wei Liu 已提交
608
void pdu_free(V9fsPDU *pdu)
609 610
{
    if (pdu) {
611
        V9fsState *s = pdu->s;
612 613 614 615 616 617 618 619
        /*
         * 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);
        }
620 621 622
    }
}

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

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

637 638 639 640 641 642 643 644 645
        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;
        }
646

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

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

    /* 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 已提交
662
    pdu_push_and_notify(pdu);
663

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

667
    pdu_free(pdu);
668 669
}

670 671 672 673 674 675 676 677 678
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;
    }

679 680 681 682 683 684 685 686 687 688
    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) {
689
        if (extension->size && extension->data[0] == 'c') {
690 691 692
            ret |= S_IFCHR;
        } else {
            ret |= S_IFBLK;
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 736
        }
    }

    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;
}

737 738 739 740 741 742 743 744 745
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);
}

746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
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;
    }

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

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

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

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

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

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

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

    return mode;
}

795
static int stat_to_v9stat(V9fsPDU *pdu, V9fsPath *name,
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
                            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);

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

818
    v9fs_string_null(&v9stat->extension);
819

820
    if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
821
        err = v9fs_co_readlink(pdu, name, &v9stat->extension);
822
        if (err < 0) {
823
            return err;
824
        }
825 826 827 828 829
    } 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)) {
830 831
        v9fs_string_sprintf(&v9stat->extension, "%s %lu",
                "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink);
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
    }

    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;
}

852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
#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,
873
                                V9fsStatDotl *v9lstat)
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
{
    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);
}

897 898 899 900 901 902 903 904 905 906 907 908 909 910
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");
}

911 912
/* Will call this only for path name based fid */
static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len)
913
{
914 915 916 917 918 919 920
    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++;
921 922
}

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

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

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

944 945
    virtfs_reset(pdu);

946 947 948 949 950
    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 {
951
        v9fs_string_sprintf(&version, "unknown");
952
    }
953

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

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

977 978 979 980 981 982 983
    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;
    }
984
    trace_v9fs_attach(pdu->tag, pdu->id, fid, afid, uname.data, aname.data);
985 986 987 988

    fidp = alloc_fid(s, fid);
    if (fidp == NULL) {
        err = -EINVAL;
989
        goto out_nofid;
990
    }
991
    fidp->uid = n_uname;
992
    err = v9fs_co_name_to_path(pdu, NULL, "/", &fidp->path);
993 994 995 996 997
    if (err < 0) {
        err = -EINVAL;
        clunk_fid(s, fid);
        goto out;
    }
998
    err = fid_to_qid(pdu, fidp, &qid);
999
    if (err < 0) {
1000
        err = -EINVAL;
1001
        clunk_fid(s, fid);
1002 1003
        goto out;
    }
1004 1005 1006 1007 1008 1009
    err = pdu_marshal(pdu, offset, "Q", &qid);
    if (err < 0) {
        clunk_fid(s, fid);
        goto out;
    }
    err += offset;
1010 1011
    trace_v9fs_attach_return(pdu->tag, pdu->id,
                             qid.type, qid.version, qid.path);
1012 1013 1014 1015 1016 1017
    /*
     * 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;
1018 1019 1020
        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);
1021 1022
        migrate_add_blocker(s->migration_blocker);
    }
1023
out:
1024
    put_fid(pdu, fidp);
1025
out_nofid:
1026
    pdu_complete(pdu, err);
1027 1028
    v9fs_string_free(&uname);
    v9fs_string_free(&aname);
1029 1030
}

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

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

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

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

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

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

    /*  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);
1111 1112 1113 1114 1115 1116 1117
        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 */
1118
            goto out;
1119 1120 1121
        default:
            /* failed to get st_gen: not fatal, ignore */
            break;
1122 1123
        }
    }
1124 1125 1126 1127 1128
    retval = pdu_marshal(pdu, offset, "A", &v9stat_dotl);
    if (retval < 0) {
        goto out;
    }
    retval += offset;
1129 1130 1131
    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);
1132 1133 1134
out:
    put_fid(pdu, fidp);
out_nofid:
1135
    pdu_complete(pdu, retval);
1136 1137
}

1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
/* 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
1150

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1489
    flags = get_dotl_openflags(pdu->s, flags);
1490
    err = v9fs_co_open2(pdu, fidp, &name, gid,
1491
                        flags | O_CREAT, mode, &stbuf);
1492 1493 1494 1495
    if (err < 0) {
        goto out;
    }
    fidp->fid_type = P9_FID_FILE;
1496 1497 1498 1499 1500 1501 1502 1503
    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;
    }
1504
    iounit =  get_iounit(pdu, &fidp->path);
1505
    stat_to_qid(&stbuf, &qid);
1506 1507 1508 1509 1510
    err = pdu_marshal(pdu, offset, "Qd", &qid, iounit);
    if (err < 0) {
        goto out;
    }
    err += offset;
1511 1512
    trace_v9fs_lcreate_return(pdu->tag, pdu->id,
                              qid.type, qid.version, qid.path, iounit);
1513
out:
1514
    put_fid(pdu, fidp);
1515
out_nofid:
1516
    pdu_complete(pdu, err);
1517
    v9fs_string_free(&name);
1518 1519
}

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

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

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

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

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

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

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

1592 1593 1594 1595 1596 1597 1598 1599 1600
    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;
1601
    }
1602 1603 1604 1605 1606
    err = pdu_marshal(pdu, offset, "d", read_count);
    if (err < 0) {
        return err;
    }
    offset += err;
W
Wei Liu 已提交
1607 1608

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

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

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

    dent = g_malloc(sizeof(struct dirent));

1637
    while (1) {
1638
        v9fs_path_init(&path);
1639
        err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
1640
        if (err || !result) {
1641 1642
            break;
        }
1643
        err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path);
1644 1645 1646
        if (err < 0) {
            goto out;
        }
1647
        err = v9fs_co_lstat(pdu, &path, &stbuf);
1648 1649 1650
        if (err < 0) {
            goto out;
        }
1651
        err = stat_to_v9stat(pdu, &path, &stbuf, &v9stat);
1652 1653
        if (err < 0) {
            goto out;
1654
        }
1655 1656 1657 1658
        /* 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 */
1659
            v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
1660
            v9fs_stat_free(&v9stat);
1661
            v9fs_path_free(&path);
1662
            g_free(dent);
1663 1664 1665 1666
            return count;
        }
        count += len;
        v9fs_stat_free(&v9stat);
1667
        v9fs_path_free(&path);
1668
        saved_dir_pos = dent->d_off;
1669 1670
    }
out:
1671
    g_free(dent);
1672
    v9fs_path_free(&path);
1673 1674
    if (err < 0) {
        return err;
1675
    }
1676
    return count;
1677 1678
}

1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690
/*
 * 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,
1691
                                    size_t skip, size_t size,
1692 1693 1694 1695 1696 1697
                                    bool is_write)
{
    QEMUIOVector elem;
    struct iovec *iov;
    unsigned int niov;

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

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

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

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

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

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

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

1789
static size_t v9fs_readdir_data_size(V9fsString *name)
1790
{
1791 1792 1793 1794 1795
    /*
     * 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);
1796 1797
}

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

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

    dent = g_malloc(sizeof(struct dirent));

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

1842 1843 1844 1845
        /* 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);
1846 1847 1848 1849 1850 1851
        if (len < 0) {
            v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
            v9fs_string_free(&name);
            g_free(dent);
            return len;
        }
1852 1853 1854 1855
        count += len;
        v9fs_string_free(&name);
        saved_dir_pos = dent->d_off;
    }
1856
    g_free(dent);
1857 1858 1859 1860
    if (err < 0) {
        return err;
    }
    return count;
1861 1862
}

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

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

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

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

1922 1923 1924 1925 1926

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

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

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

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

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

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

2058
    v9fs_path_init(&path);
2059 2060 2061 2062 2063 2064 2065
    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;
    }
2066 2067
    trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode);

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

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

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

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

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

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

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

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

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

    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;
2286
        pdu_free(cancel_pdu);
2287
    }
2288
    pdu_complete(pdu, 7);
2289 2290
}

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

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

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

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

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

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

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

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

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

2419 2420

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

2432
    v9fs_path_init(&new_path);
2433
    if (newdirfid != -1) {
2434
        dirfidp = get_fid(pdu, newdirfid);
2435 2436
        if (dirfidp == NULL) {
            err = -ENOENT;
2437
            goto out_nofid;
2438
        }
2439
        BUG_ON(dirfidp->fid_type != P9_FID_NONE);
2440
        v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path);
2441
    } else {
2442
        old_name = fidp->path.data;
2443 2444 2445 2446 2447 2448
        end = strrchr(old_name, '/');
        if (end) {
            end++;
        } else {
            end = old_name;
        }
2449
        new_name = g_malloc0(end - old_name + name->size + 1);
2450
        strncat(new_name, old_name, end - old_name);
2451
        strncat(new_name + (end - old_name), name->data, name->size);
2452
        v9fs_co_name_to_path(pdu, NULL, new_name, &new_path);
2453
        g_free(new_name);
2454
    }
2455
    err = v9fs_co_rename(pdu, &fidp->path, &new_path);
2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466
    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));
2467 2468
        }
    }
2469
out:
2470
    if (dirfidp) {
2471
        put_fid(pdu, dirfidp);
2472
    }
2473
    v9fs_path_free(&new_path);
2474
out_nofid:
2475 2476 2477
    return err;
}

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

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

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


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

    /*
     * 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);
}

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

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

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

2599 2600 2601 2602 2603 2604 2605
    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;
    }
2606

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

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

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

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

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

2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726
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;
2727 2728 2729 2730 2731 2732
    int32_t bsize_factor;

    /*
     * compute bsize factor based on host file system block size
     * and client msize
     */
2733
    bsize_factor = (s->msize - P9_IOHDRSZ)/stbuf->f_bsize;
2734 2735 2736
    if (!bsize_factor) {
        bsize_factor = 1;
    }
2737 2738 2739
    f_type  = stbuf->f_type;
    f_bsize = stbuf->f_bsize;
    f_bsize *= bsize_factor;
2740 2741 2742 2743 2744
    /*
     * 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
     */
2745 2746 2747 2748 2749 2750 2751 2752
    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;
2753

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

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

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

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

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

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

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

M
M. Mohan Kumar 已提交
2842 2843 2844 2845 2846 2847 2848 2849
/*
 * 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
 */
2850
static void v9fs_lock(void *opaque)
M
M. Mohan Kumar 已提交
2851
{
2852
    int8_t status;
2853
    V9fsFlock flock;
2854 2855 2856 2857
    size_t offset = 7;
    struct stat stbuf;
    V9fsFidState *fidp;
    int32_t fid, err = 0;
2858
    V9fsPDU *pdu = opaque;
M
M. Mohan Kumar 已提交
2859

2860 2861 2862 2863 2864 2865 2866 2867
    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;
    }
2868
    trace_v9fs_lock(pdu->tag, pdu->id, fid,
2869
                    flock.type, flock.start, flock.length);
2870

M
M. Mohan Kumar 已提交
2871 2872

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

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

2912 2913 2914 2915 2916 2917 2918
    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;
    }
2919
    trace_v9fs_getlock(pdu->tag, pdu->id, fid,
2920
                       glock.type, glock.start, glock.length);
2921

2922
    fidp = get_fid(pdu, fid);
2923
    if (fidp == NULL) {
M
M. Mohan Kumar 已提交
2924
        err = -ENOENT;
2925
        goto out_nofid;
M
M. Mohan Kumar 已提交
2926
    }
2927
    err = v9fs_co_fstat(pdu, fidp, &stbuf);
M
M. Mohan Kumar 已提交
2928 2929 2930
    if (err < 0) {
        goto out;
    }
2931 2932 2933 2934 2935 2936 2937 2938 2939 2940
    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 已提交
2941
out:
2942
    put_fid(pdu, fidp);
2943
out_nofid:
2944
    pdu_complete(pdu, err);
2945
    v9fs_string_free(&glock.client_id);
M
M. Mohan Kumar 已提交
2946 2947
}

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

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

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

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

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

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

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

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

3117
    file_fidp = get_fid(pdu, fid);
3118
    if (file_fidp == NULL) {
3119
        err = -EINVAL;
3120
        goto out_nofid;
3121 3122
    }
    /* Make the file fid point to xattr */
3123 3124 3125 3126 3127 3128 3129
    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);
3130
    xattr_fidp->fs.xattr.value = g_malloc(size);
3131
    err = offset;
3132
    put_fid(pdu, file_fidp);
3133
out_nofid:
3134
    pdu_complete(pdu, err);
3135
    v9fs_string_free(&name);
3136
}
3137

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

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

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

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

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

3220 3221 3222
static void v9fs_fs_ro(void *opaque)
{
    V9fsPDU *pdu = opaque;
3223
    pdu_complete(pdu, -EROFS);
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 3252
}

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 已提交
3253
void pdu_submit(V9fsPDU *pdu)
3254
{
3255 3256
    Coroutine *co;
    CoroutineEntry *handler;
3257
    V9fsState *s = pdu->s;
3258

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

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

3273 3274 3275
/* Returns 0 on success, 1 on failure. */
int v9fs_device_realize_common(V9fsState *s, Error **errp)
{
W
Wei Liu 已提交
3276
    V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
3277 3278 3279 3280 3281 3282 3283 3284 3285 3286
    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 已提交
3287 3288 3289
        QLIST_INSERT_HEAD(&s->free_list, &v->pdus[i], next);
        v->pdus[i].s = s;
        v->pdus[i].idx = i;
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 3369
    }

    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);
}

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