qemu/hw/9pfs/9p-synth.c
<<
>>
Prefs
   1/*
   2 * 9p synthetic file system support
   3 *
   4 * Copyright IBM, Corp. 2011
   5 *
   6 * Authors:
   7 *  Malahal Naineni <malahal@us.ibm.com>
   8 *  Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
   9 *
  10 * This work is licensed under the terms of the GNU GPL, version 2.  See
  11 * the COPYING file in the top-level directory.
  12 *
  13 */
  14
  15/*
  16 * Not so fast! You might want to read the 9p developer docs first:
  17 * https://wiki.qemu.org/Documentation/9p
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "9p.h"
  22#include "fsdev/qemu-fsdev.h"
  23#include "9p-synth.h"
  24#include "qemu/rcu.h"
  25#include "qemu/rcu_queue.h"
  26#include "qemu/cutils.h"
  27#include "sysemu/qtest.h"
  28
  29/* Root node for synth file system */
  30static V9fsSynthNode synth_root = {
  31    .name = "/",
  32    .actual_attr = {
  33        .mode = 0555 | S_IFDIR,
  34        .nlink = 1,
  35    },
  36    .attr = &synth_root.actual_attr,
  37};
  38
  39static QemuMutex  synth_mutex;
  40static int synth_node_count;
  41/* set to 1 when the synth fs is ready */
  42static int synth_fs;
  43
  44static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode,
  45                                        const char *name,
  46                                        V9fsSynthNodeAttr *attr, int inode)
  47{
  48    V9fsSynthNode *node;
  49
  50    /* Add directory type and remove write bits */
  51    mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH);
  52    node = g_new0(V9fsSynthNode, 1);
  53    if (attr) {
  54        /* We are adding .. or . entries */
  55        node->attr = attr;
  56        node->attr->nlink++;
  57    } else {
  58        node->attr = &node->actual_attr;
  59        node->attr->inode = inode;
  60        node->attr->nlink = 1;
  61        /* We don't allow write to directories */
  62        node->attr->mode   = mode;
  63        node->attr->write = NULL;
  64        node->attr->read  = NULL;
  65    }
  66    node->private = node;
  67    pstrcpy(node->name, sizeof(node->name), name);
  68    QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
  69    return node;
  70}
  71
  72int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
  73                          const char *name, V9fsSynthNode **result)
  74{
  75    V9fsSynthNode *node, *tmp;
  76
  77    if (!synth_fs) {
  78        return -EAGAIN;
  79    }
  80    if (!name || (strlen(name) >= NAME_MAX)) {
  81        return -EINVAL;
  82    }
  83    if (!parent) {
  84        parent = &synth_root;
  85    }
  86    QEMU_LOCK_GUARD(&synth_mutex);
  87    QLIST_FOREACH(tmp, &parent->child, sibling) {
  88        if (!strcmp(tmp->name, name)) {
  89            return -EEXIST;
  90        }
  91    }
  92    /* Add the name */
  93    node = v9fs_add_dir_node(parent, mode, name, NULL, ++synth_node_count);
  94    v9fs_add_dir_node(node, parent->attr->mode, "..",
  95                      parent->attr, parent->attr->inode);
  96    v9fs_add_dir_node(node, node->attr->mode, ".",
  97                      node->attr, node->attr->inode);
  98    *result = node;
  99    return 0;
 100}
 101
 102int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
 103                             const char *name, v9fs_synth_read read,
 104                             v9fs_synth_write write, void *arg)
 105{
 106    V9fsSynthNode *node, *tmp;
 107
 108    if (!synth_fs) {
 109        return -EAGAIN;
 110    }
 111    if (!name || (strlen(name) >= NAME_MAX)) {
 112        return -EINVAL;
 113    }
 114    if (!parent) {
 115        parent = &synth_root;
 116    }
 117
 118    QEMU_LOCK_GUARD(&synth_mutex);
 119    QLIST_FOREACH(tmp, &parent->child, sibling) {
 120        if (!strcmp(tmp->name, name)) {
 121            return -EEXIST;
 122        }
 123    }
 124    /* Add file type and remove write bits */
 125    mode = ((mode & 0777) | S_IFREG);
 126    node = g_new0(V9fsSynthNode, 1);
 127    node->attr         = &node->actual_attr;
 128    node->attr->inode  = ++synth_node_count;
 129    node->attr->nlink  = 1;
 130    node->attr->read   = read;
 131    node->attr->write  = write;
 132    node->attr->mode   = mode;
 133    node->private      = arg;
 134    pstrcpy(node->name, sizeof(node->name), name);
 135    QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
 136    return 0;
 137}
 138
 139static void synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf)
 140{
 141    stbuf->st_dev = 0;
 142    stbuf->st_ino = node->attr->inode;
 143    stbuf->st_mode = node->attr->mode;
 144    stbuf->st_nlink = node->attr->nlink;
 145    stbuf->st_uid = 0;
 146    stbuf->st_gid = 0;
 147    stbuf->st_rdev = 0;
 148    stbuf->st_size = 0;
 149    stbuf->st_blksize = 0;
 150    stbuf->st_blocks = 0;
 151    stbuf->st_atime = 0;
 152    stbuf->st_mtime = 0;
 153    stbuf->st_ctime = 0;
 154}
 155
 156static int synth_lstat(FsContext *fs_ctx,
 157                            V9fsPath *fs_path, struct stat *stbuf)
 158{
 159    V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
 160
 161    synth_fill_statbuf(node, stbuf);
 162    return 0;
 163}
 164
 165static int synth_fstat(FsContext *fs_ctx, int fid_type,
 166                            V9fsFidOpenState *fs, struct stat *stbuf)
 167{
 168    V9fsSynthOpenState *synth_open = fs->private;
 169    synth_fill_statbuf(synth_open->node, stbuf);
 170    return 0;
 171}
 172
 173static int synth_opendir(FsContext *ctx,
 174                             V9fsPath *fs_path, V9fsFidOpenState *fs)
 175{
 176    V9fsSynthOpenState *synth_open;
 177    V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
 178
 179    /*
 180     * V9fsSynthOpenState contains 'struct dirent' which have OS-specific
 181     * properties, thus it's zero cleared on allocation here and below
 182     * in synth_open.
 183     */
 184    synth_open = g_new0(V9fsSynthOpenState, 1);
 185    synth_open->node = node;
 186    node->open_count++;
 187    fs->private = synth_open;
 188    return 0;
 189}
 190
 191static int synth_closedir(FsContext *ctx, V9fsFidOpenState *fs)
 192{
 193    V9fsSynthOpenState *synth_open = fs->private;
 194    V9fsSynthNode *node = synth_open->node;
 195
 196    node->open_count--;
 197    g_free(synth_open);
 198    fs->private = NULL;
 199    return 0;
 200}
 201
 202static off_t synth_telldir(FsContext *ctx, V9fsFidOpenState *fs)
 203{
 204    V9fsSynthOpenState *synth_open = fs->private;
 205    return synth_open->offset;
 206}
 207
 208static void synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
 209{
 210    V9fsSynthOpenState *synth_open = fs->private;
 211    synth_open->offset = off;
 212}
 213
 214static void synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
 215{
 216    synth_seekdir(ctx, fs, 0);
 217}
 218
 219static void synth_direntry(V9fsSynthNode *node,
 220                                struct dirent *entry, off_t off)
 221{
 222    size_t sz = strlen(node->name) + 1;
 223    /*
 224     * 'entry' is always inside of V9fsSynthOpenState which have NAME_MAX
 225     * back padding. Ensure we do not overflow it.
 226     */
 227    g_assert(sizeof(struct dirent) + NAME_MAX >=
 228             offsetof(struct dirent, d_name) + sz);
 229    memcpy(entry->d_name, node->name, sz);
 230    entry->d_ino = node->attr->inode;
 231#ifdef CONFIG_DARWIN
 232    entry->d_seekoff = off + 1;
 233#else
 234    entry->d_off = off + 1;
 235#endif
 236}
 237
 238static struct dirent *synth_get_dentry(V9fsSynthNode *dir,
 239                                            struct dirent *entry, off_t off)
 240{
 241    int i = 0;
 242    V9fsSynthNode *node;
 243
 244    rcu_read_lock();
 245    QLIST_FOREACH(node, &dir->child, sibling) {
 246        /* This is the off child of the directory */
 247        if (i == off) {
 248            break;
 249        }
 250        i++;
 251    }
 252    rcu_read_unlock();
 253    if (!node) {
 254        /* end of directory */
 255        return NULL;
 256    }
 257    synth_direntry(node, entry, off);
 258    return entry;
 259}
 260
 261static struct dirent *synth_readdir(FsContext *ctx, V9fsFidOpenState *fs)
 262{
 263    struct dirent *entry;
 264    V9fsSynthOpenState *synth_open = fs->private;
 265    V9fsSynthNode *node = synth_open->node;
 266    entry = synth_get_dentry(node, &synth_open->dent, synth_open->offset);
 267    if (entry) {
 268        synth_open->offset++;
 269    }
 270    return entry;
 271}
 272
 273static int synth_open(FsContext *ctx, V9fsPath *fs_path,
 274                           int flags, V9fsFidOpenState *fs)
 275{
 276    V9fsSynthOpenState *synth_open;
 277    V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
 278
 279    synth_open = g_new0(V9fsSynthOpenState, 1);
 280    synth_open->node = node;
 281    node->open_count++;
 282    fs->private = synth_open;
 283    return 0;
 284}
 285
 286static int synth_open2(FsContext *fs_ctx, V9fsPath *dir_path,
 287                            const char *name, int flags,
 288                            FsCred *credp, V9fsFidOpenState *fs)
 289{
 290    errno = ENOSYS;
 291    return -1;
 292}
 293
 294static int synth_close(FsContext *ctx, V9fsFidOpenState *fs)
 295{
 296    V9fsSynthOpenState *synth_open = fs->private;
 297    V9fsSynthNode *node = synth_open->node;
 298
 299    node->open_count--;
 300    g_free(synth_open);
 301    fs->private = NULL;
 302    return 0;
 303}
 304
 305static ssize_t synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
 306                                  const struct iovec *iov,
 307                                  int iovcnt, off_t offset)
 308{
 309    int i, count = 0, wcount;
 310    V9fsSynthOpenState *synth_open = fs->private;
 311    V9fsSynthNode *node = synth_open->node;
 312    if (!node->attr->write) {
 313        errno = EPERM;
 314        return -1;
 315    }
 316    for (i = 0; i < iovcnt; i++) {
 317        wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len,
 318                                   offset, node->private);
 319        offset += wcount;
 320        count  += wcount;
 321        /* If we wrote less than requested. we are done */
 322        if (wcount < iov[i].iov_len) {
 323            break;
 324        }
 325    }
 326    return count;
 327}
 328
 329static ssize_t synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
 330                                 const struct iovec *iov,
 331                                 int iovcnt, off_t offset)
 332{
 333    int i, count = 0, rcount;
 334    V9fsSynthOpenState *synth_open = fs->private;
 335    V9fsSynthNode *node = synth_open->node;
 336    if (!node->attr->read) {
 337        errno = EPERM;
 338        return -1;
 339    }
 340    for (i = 0; i < iovcnt; i++) {
 341        rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len,
 342                                  offset, node->private);
 343        offset += rcount;
 344        count  += rcount;
 345        /* If we read less than requested. we are done */
 346        if (rcount < iov[i].iov_len) {
 347            break;
 348        }
 349    }
 350    return count;
 351}
 352
 353static int synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
 354{
 355    errno = ENOSYS;
 356    return -1;
 357}
 358
 359static int synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
 360{
 361    errno = EPERM;
 362    return -1;
 363}
 364
 365static int synth_mknod(FsContext *fs_ctx, V9fsPath *path,
 366                       const char *buf, FsCred *credp)
 367{
 368    errno = EPERM;
 369    return -1;
 370}
 371
 372static int synth_mkdir(FsContext *fs_ctx, V9fsPath *path,
 373                       const char *buf, FsCred *credp)
 374{
 375    errno = EPERM;
 376    return -1;
 377}
 378
 379static ssize_t synth_readlink(FsContext *fs_ctx, V9fsPath *path,
 380                                   char *buf, size_t bufsz)
 381{
 382    errno = ENOSYS;
 383    return -1;
 384}
 385
 386static int synth_symlink(FsContext *fs_ctx, const char *oldpath,
 387                              V9fsPath *newpath, const char *buf, FsCred *credp)
 388{
 389    errno = EPERM;
 390    return -1;
 391}
 392
 393static int synth_link(FsContext *fs_ctx, V9fsPath *oldpath,
 394                           V9fsPath *newpath, const char *buf)
 395{
 396    errno = EPERM;
 397    return -1;
 398}
 399
 400static int synth_rename(FsContext *ctx, const char *oldpath,
 401                             const char *newpath)
 402{
 403    errno = EPERM;
 404    return -1;
 405}
 406
 407static int synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
 408{
 409    errno = EPERM;
 410    return -1;
 411}
 412
 413static int synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
 414                                const struct timespec *buf)
 415{
 416    errno = EPERM;
 417    return 0;
 418}
 419
 420static int synth_remove(FsContext *ctx, const char *path)
 421{
 422    errno = EPERM;
 423    return -1;
 424}
 425
 426static int synth_fsync(FsContext *ctx, int fid_type,
 427                            V9fsFidOpenState *fs, int datasync)
 428{
 429    errno = ENOSYS;
 430    return 0;
 431}
 432
 433static int synth_statfs(FsContext *s, V9fsPath *fs_path,
 434                             struct statfs *stbuf)
 435{
 436    stbuf->f_type = 0xABCD;
 437    stbuf->f_bsize = 512;
 438    stbuf->f_blocks = 0;
 439    stbuf->f_files = synth_node_count;
 440#ifndef CONFIG_DARWIN
 441    stbuf->f_namelen = NAME_MAX;
 442#endif
 443    return 0;
 444}
 445
 446static ssize_t synth_lgetxattr(FsContext *ctx, V9fsPath *path,
 447                                    const char *name, void *value, size_t size)
 448{
 449    errno = ENOTSUP;
 450    return -1;
 451}
 452
 453static ssize_t synth_llistxattr(FsContext *ctx, V9fsPath *path,
 454                                     void *value, size_t size)
 455{
 456    errno = ENOTSUP;
 457    return -1;
 458}
 459
 460static int synth_lsetxattr(FsContext *ctx, V9fsPath *path,
 461                                const char *name, void *value,
 462                                size_t size, int flags)
 463{
 464    errno = ENOTSUP;
 465    return -1;
 466}
 467
 468static int synth_lremovexattr(FsContext *ctx,
 469                                   V9fsPath *path, const char *name)
 470{
 471    errno = ENOTSUP;
 472    return -1;
 473}
 474
 475static int synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
 476                                   const char *name, V9fsPath *target)
 477{
 478    V9fsSynthNode *node;
 479    V9fsSynthNode *dir_node;
 480
 481    /* "." and ".." are not allowed */
 482    if (!strcmp(name, ".") || !strcmp(name, "..")) {
 483        errno = EINVAL;
 484        return -1;
 485
 486    }
 487    if (!dir_path) {
 488        dir_node = &synth_root;
 489    } else {
 490        dir_node = *(V9fsSynthNode **)dir_path->data;
 491    }
 492    if (!strcmp(name, "/")) {
 493        node = dir_node;
 494        goto out;
 495    }
 496    /* search for the name in the childern */
 497    rcu_read_lock();
 498    QLIST_FOREACH(node, &dir_node->child, sibling) {
 499        if (!strcmp(node->name, name)) {
 500            break;
 501        }
 502    }
 503    rcu_read_unlock();
 504
 505    if (!node) {
 506        errno = ENOENT;
 507        return -1;
 508    }
 509out:
 510    /* Copy the node pointer to fid */
 511    g_free(target->data);
 512    target->data = g_memdup(&node, sizeof(void *));
 513    target->size = sizeof(void *);
 514    return 0;
 515}
 516
 517static int synth_renameat(FsContext *ctx, V9fsPath *olddir,
 518                               const char *old_name, V9fsPath *newdir,
 519                               const char *new_name)
 520{
 521    errno = EPERM;
 522    return -1;
 523}
 524
 525static int synth_unlinkat(FsContext *ctx, V9fsPath *dir,
 526                               const char *name, int flags)
 527{
 528    errno = EPERM;
 529    return -1;
 530}
 531
 532static ssize_t v9fs_synth_qtest_write(void *buf, int len, off_t offset,
 533                                      void *arg)
 534{
 535    return 1;
 536}
 537
 538static ssize_t v9fs_synth_qtest_flush_write(void *buf, int len, off_t offset,
 539                                            void *arg)
 540{
 541    bool should_block = !!*(uint8_t *)buf;
 542
 543    if (should_block) {
 544        /* This will cause the server to call us again until we're cancelled */
 545        errno = EINTR;
 546        return -1;
 547    }
 548
 549    return 1;
 550}
 551
 552static int synth_init(FsContext *ctx, Error **errp)
 553{
 554    QLIST_INIT(&synth_root.child);
 555    qemu_mutex_init(&synth_mutex);
 556
 557    /* Add "." and ".." entries for root */
 558    v9fs_add_dir_node(&synth_root, synth_root.attr->mode,
 559                      "..", synth_root.attr, synth_root.attr->inode);
 560    v9fs_add_dir_node(&synth_root, synth_root.attr->mode,
 561                      ".", synth_root.attr, synth_root.attr->inode);
 562
 563    /* Mark the subsystem is ready for use */
 564    synth_fs = 1;
 565
 566    if (qtest_enabled()) {
 567        V9fsSynthNode *node = NULL;
 568        int i, ret;
 569
 570        /* Directory hierarchy for WALK test */
 571        for (i = 0; i < P9_MAXWELEM; i++) {
 572            char *name = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i);
 573
 574            ret = qemu_v9fs_synth_mkdir(node, 0700, name, &node);
 575            assert(!ret);
 576            g_free(name);
 577        }
 578
 579        /* File for LOPEN test */
 580        ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_LOPEN_FILE,
 581                                       NULL, NULL, ctx);
 582        assert(!ret);
 583
 584        /* File for WRITE test */
 585        ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_WRITE_FILE,
 586                                       NULL, v9fs_synth_qtest_write, ctx);
 587        assert(!ret);
 588
 589        /* File for FLUSH test */
 590        ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_FLUSH_FILE,
 591                                       NULL, v9fs_synth_qtest_flush_write,
 592                                       ctx);
 593        assert(!ret);
 594
 595        /* Directory for READDIR test */
 596        {
 597            V9fsSynthNode *dir = NULL;
 598            ret = qemu_v9fs_synth_mkdir(
 599                NULL, 0700, QTEST_V9FS_SYNTH_READDIR_DIR, &dir
 600            );
 601            assert(!ret);
 602            for (i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) {
 603                char *name = g_strdup_printf(
 604                    QTEST_V9FS_SYNTH_READDIR_FILE, i
 605                );
 606                ret = qemu_v9fs_synth_add_file(
 607                    dir, 0, name, NULL, NULL, ctx
 608                );
 609                assert(!ret);
 610                g_free(name);
 611            }
 612        }
 613    }
 614
 615    return 0;
 616}
 617
 618FileOperations synth_ops = {
 619    .init         = synth_init,
 620    .lstat        = synth_lstat,
 621    .readlink     = synth_readlink,
 622    .close        = synth_close,
 623    .closedir     = synth_closedir,
 624    .open         = synth_open,
 625    .opendir      = synth_opendir,
 626    .rewinddir    = synth_rewinddir,
 627    .telldir      = synth_telldir,
 628    .readdir      = synth_readdir,
 629    .seekdir      = synth_seekdir,
 630    .preadv       = synth_preadv,
 631    .pwritev      = synth_pwritev,
 632    .chmod        = synth_chmod,
 633    .mknod        = synth_mknod,
 634    .mkdir        = synth_mkdir,
 635    .fstat        = synth_fstat,
 636    .open2        = synth_open2,
 637    .symlink      = synth_symlink,
 638    .link         = synth_link,
 639    .truncate     = synth_truncate,
 640    .rename       = synth_rename,
 641    .chown        = synth_chown,
 642    .utimensat    = synth_utimensat,
 643    .remove       = synth_remove,
 644    .fsync        = synth_fsync,
 645    .statfs       = synth_statfs,
 646    .lgetxattr    = synth_lgetxattr,
 647    .llistxattr   = synth_llistxattr,
 648    .lsetxattr    = synth_lsetxattr,
 649    .lremovexattr = synth_lremovexattr,
 650    .name_to_path = synth_name_to_path,
 651    .renameat     = synth_renameat,
 652    .unlinkat     = synth_unlinkat,
 653};
 654