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