qemu/hw/9pfs/9p-synth.c
<<
>>
Prefs
   1/*
   2 * Virtio 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 "hw/virtio/virtio.h"
  17#include "9p.h"
  18#include "9p-xattr.h"
  19#include "fsdev/qemu-fsdev.h"
  20#include "9p-synth.h"
  21#include "qemu/rcu.h"
  22#include "qemu/rcu_queue.h"
  23#include "qemu/cutils.h"
  24
  25/* Root node for synth file system */
  26static V9fsSynthNode v9fs_synth_root = {
  27    .name = "/",
  28    .actual_attr = {
  29        .mode = 0555 | S_IFDIR,
  30        .nlink = 1,
  31    },
  32    .attr = &v9fs_synth_root.actual_attr,
  33};
  34
  35static QemuMutex  v9fs_synth_mutex;
  36static int v9fs_synth_node_count;
  37/* set to 1 when the synth fs is ready */
  38static int v9fs_synth_fs;
  39
  40static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode,
  41                                        const char *name,
  42                                        V9fsSynthNodeAttr *attr, int inode)
  43{
  44    V9fsSynthNode *node;
  45
  46    /* Add directory type and remove write bits */
  47    mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH);
  48    node = g_malloc0(sizeof(V9fsSynthNode));
  49    if (attr) {
  50        /* We are adding .. or . entries */
  51        node->attr = attr;
  52        node->attr->nlink++;
  53    } else {
  54        node->attr = &node->actual_attr;
  55        node->attr->inode = inode;
  56        node->attr->nlink = 1;
  57        /* We don't allow write to directories */
  58        node->attr->mode   = mode;
  59        node->attr->write = NULL;
  60        node->attr->read  = NULL;
  61    }
  62    node->private = node;
  63    pstrcpy(node->name, sizeof(node->name), name);
  64    QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
  65    return node;
  66}
  67
  68int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
  69                          const char *name, V9fsSynthNode **result)
  70{
  71    int ret;
  72    V9fsSynthNode *node, *tmp;
  73
  74    if (!v9fs_synth_fs) {
  75        return EAGAIN;
  76    }
  77    if (!name || (strlen(name) >= NAME_MAX)) {
  78        return EINVAL;
  79    }
  80    if (!parent) {
  81        parent = &v9fs_synth_root;
  82    }
  83    qemu_mutex_lock(&v9fs_synth_mutex);
  84    QLIST_FOREACH(tmp, &parent->child, sibling) {
  85        if (!strcmp(tmp->name, name)) {
  86            ret = EEXIST;
  87            goto err_out;
  88        }
  89    }
  90    /* Add the name */
  91    node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++);
  92    v9fs_add_dir_node(node, parent->attr->mode, "..",
  93                      parent->attr, parent->attr->inode);
  94    v9fs_add_dir_node(node, node->attr->mode, ".",
  95                      node->attr, node->attr->inode);
  96    *result = node;
  97    ret = 0;
  98err_out:
  99    qemu_mutex_unlock(&v9fs_synth_mutex);
 100    return ret;
 101}
 102
 103int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
 104                             const char *name, v9fs_synth_read read,
 105                             v9fs_synth_write write, void *arg)
 106{
 107    int ret;
 108    V9fsSynthNode *node, *tmp;
 109
 110    if (!v9fs_synth_fs) {
 111        return EAGAIN;
 112    }
 113    if (!name || (strlen(name) >= NAME_MAX)) {
 114        return EINVAL;
 115    }
 116    if (!parent) {
 117        parent = &v9fs_synth_root;
 118    }
 119
 120    qemu_mutex_lock(&v9fs_synth_mutex);
 121    QLIST_FOREACH(tmp, &parent->child, sibling) {
 122        if (!strcmp(tmp->name, name)) {
 123            ret = EEXIST;
 124            goto err_out;
 125        }
 126    }
 127    /* Add file type and remove write bits */
 128    mode = ((mode & 0777) | S_IFREG);
 129    node = g_malloc0(sizeof(V9fsSynthNode));
 130    node->attr         = &node->actual_attr;
 131    node->attr->inode  = v9fs_synth_node_count++;
 132    node->attr->nlink  = 1;
 133    node->attr->read   = read;
 134    node->attr->write  = write;
 135    node->attr->mode   = mode;
 136    node->private      = arg;
 137    pstrcpy(node->name, sizeof(node->name), name);
 138    QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
 139    ret = 0;
 140err_out:
 141    qemu_mutex_unlock(&v9fs_synth_mutex);
 142    return ret;
 143}
 144
 145static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf)
 146{
 147    stbuf->st_dev = 0;
 148    stbuf->st_ino = node->attr->inode;
 149    stbuf->st_mode = node->attr->mode;
 150    stbuf->st_nlink = node->attr->nlink;
 151    stbuf->st_uid = 0;
 152    stbuf->st_gid = 0;
 153    stbuf->st_rdev = 0;
 154    stbuf->st_size = 0;
 155    stbuf->st_blksize = 0;
 156    stbuf->st_blocks = 0;
 157    stbuf->st_atime = 0;
 158    stbuf->st_mtime = 0;
 159    stbuf->st_ctime = 0;
 160}
 161
 162static int v9fs_synth_lstat(FsContext *fs_ctx,
 163                            V9fsPath *fs_path, struct stat *stbuf)
 164{
 165    V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
 166
 167    v9fs_synth_fill_statbuf(node, stbuf);
 168    return 0;
 169}
 170
 171static int v9fs_synth_fstat(FsContext *fs_ctx, int fid_type,
 172                            V9fsFidOpenState *fs, struct stat *stbuf)
 173{
 174    V9fsSynthOpenState *synth_open = fs->private;
 175    v9fs_synth_fill_statbuf(synth_open->node, stbuf);
 176    return 0;
 177}
 178
 179static int v9fs_synth_opendir(FsContext *ctx,
 180                             V9fsPath *fs_path, V9fsFidOpenState *fs)
 181{
 182    V9fsSynthOpenState *synth_open;
 183    V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
 184
 185    synth_open = g_malloc(sizeof(*synth_open));
 186    synth_open->node = node;
 187    node->open_count++;
 188    fs->private = synth_open;
 189    return 0;
 190}
 191
 192static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs)
 193{
 194    V9fsSynthOpenState *synth_open = fs->private;
 195    V9fsSynthNode *node = synth_open->node;
 196
 197    node->open_count--;
 198    g_free(synth_open);
 199    fs->private = NULL;
 200    return 0;
 201}
 202
 203static off_t v9fs_synth_telldir(FsContext *ctx, V9fsFidOpenState *fs)
 204{
 205    V9fsSynthOpenState *synth_open = fs->private;
 206    return synth_open->offset;
 207}
 208
 209static void v9fs_synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
 210{
 211    V9fsSynthOpenState *synth_open = fs->private;
 212    synth_open->offset = off;
 213}
 214
 215static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
 216{
 217    v9fs_synth_seekdir(ctx, fs, 0);
 218}
 219
 220static void v9fs_synth_direntry(V9fsSynthNode *node,
 221                                struct dirent *entry, off_t off)
 222{
 223    strcpy(entry->d_name, node->name);
 224    entry->d_ino = node->attr->inode;
 225    entry->d_off = off + 1;
 226}
 227
 228static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry,
 229                                 struct dirent **result, off_t off)
 230{
 231    int i = 0;
 232    V9fsSynthNode *node;
 233
 234    rcu_read_lock();
 235    QLIST_FOREACH(node, &dir->child, sibling) {
 236        /* This is the off child of the directory */
 237        if (i == off) {
 238            break;
 239        }
 240        i++;
 241    }
 242    rcu_read_unlock();
 243    if (!node) {
 244        /* end of directory */
 245        *result = NULL;
 246        return 0;
 247    }
 248    v9fs_synth_direntry(node, entry, off);
 249    *result = entry;
 250    return 0;
 251}
 252
 253static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
 254                                struct dirent *entry, struct dirent **result)
 255{
 256    int ret;
 257    V9fsSynthOpenState *synth_open = fs->private;
 258    V9fsSynthNode *node = synth_open->node;
 259    ret = v9fs_synth_get_dentry(node, entry, result, synth_open->offset);
 260    if (!ret && *result != NULL) {
 261        synth_open->offset++;
 262    }
 263    return ret;
 264}
 265
 266static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path,
 267                           int flags, V9fsFidOpenState *fs)
 268{
 269    V9fsSynthOpenState *synth_open;
 270    V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
 271
 272    synth_open = g_malloc(sizeof(*synth_open));
 273    synth_open->node = node;
 274    node->open_count++;
 275    fs->private = synth_open;
 276    return 0;
 277}
 278
 279static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path,
 280                            const char *name, int flags,
 281                            FsCred *credp, V9fsFidOpenState *fs)
 282{
 283    errno = ENOSYS;
 284    return -1;
 285}
 286
 287static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs)
 288{
 289    V9fsSynthOpenState *synth_open = fs->private;
 290    V9fsSynthNode *node = synth_open->node;
 291
 292    node->open_count--;
 293    g_free(synth_open);
 294    fs->private = NULL;
 295    return 0;
 296}
 297
 298static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
 299                                  const struct iovec *iov,
 300                                  int iovcnt, off_t offset)
 301{
 302    int i, count = 0, wcount;
 303    V9fsSynthOpenState *synth_open = fs->private;
 304    V9fsSynthNode *node = synth_open->node;
 305    if (!node->attr->write) {
 306        errno = EPERM;
 307        return -1;
 308    }
 309    for (i = 0; i < iovcnt; i++) {
 310        wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len,
 311                                   offset, node->private);
 312        offset += wcount;
 313        count  += wcount;
 314        /* If we wrote less than requested. we are done */
 315        if (wcount < iov[i].iov_len) {
 316            break;
 317        }
 318    }
 319    return count;
 320}
 321
 322static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
 323                                 const struct iovec *iov,
 324                                 int iovcnt, off_t offset)
 325{
 326    int i, count = 0, rcount;
 327    V9fsSynthOpenState *synth_open = fs->private;
 328    V9fsSynthNode *node = synth_open->node;
 329    if (!node->attr->read) {
 330        errno = EPERM;
 331        return -1;
 332    }
 333    for (i = 0; i < iovcnt; i++) {
 334        rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len,
 335                                  offset, node->private);
 336        offset += rcount;
 337        count  += rcount;
 338        /* If we read less than requested. we are done */
 339        if (rcount < iov[i].iov_len) {
 340            break;
 341        }
 342    }
 343    return count;
 344}
 345
 346static int v9fs_synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
 347{
 348    errno = ENOSYS;
 349    return -1;
 350}
 351
 352static int v9fs_synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
 353{
 354    errno = EPERM;
 355    return -1;
 356}
 357
 358static int v9fs_synth_mknod(FsContext *fs_ctx, V9fsPath *path,
 359                       const char *buf, FsCred *credp)
 360{
 361    errno = EPERM;
 362    return -1;
 363}
 364
 365static int v9fs_synth_mkdir(FsContext *fs_ctx, V9fsPath *path,
 366                       const char *buf, FsCred *credp)
 367{
 368    errno = EPERM;
 369    return -1;
 370}
 371
 372static ssize_t v9fs_synth_readlink(FsContext *fs_ctx, V9fsPath *path,
 373                                   char *buf, size_t bufsz)
 374{
 375    errno = ENOSYS;
 376    return -1;
 377}
 378
 379static int v9fs_synth_symlink(FsContext *fs_ctx, const char *oldpath,
 380                              V9fsPath *newpath, const char *buf, FsCred *credp)
 381{
 382    errno = EPERM;
 383    return -1;
 384}
 385
 386static int v9fs_synth_link(FsContext *fs_ctx, V9fsPath *oldpath,
 387                           V9fsPath *newpath, const char *buf)
 388{
 389    errno = EPERM;
 390    return -1;
 391}
 392
 393static int v9fs_synth_rename(FsContext *ctx, const char *oldpath,
 394                             const char *newpath)
 395{
 396    errno = EPERM;
 397    return -1;
 398}
 399
 400static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
 401{
 402    errno = EPERM;
 403    return -1;
 404}
 405
 406static int v9fs_synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
 407                                const struct timespec *buf)
 408{
 409    errno = EPERM;
 410    return 0;
 411}
 412
 413static int v9fs_synth_remove(FsContext *ctx, const char *path)
 414{
 415    errno = EPERM;
 416    return -1;
 417}
 418
 419static int v9fs_synth_fsync(FsContext *ctx, int fid_type,
 420                            V9fsFidOpenState *fs, int datasync)
 421{
 422    errno = ENOSYS;
 423    return 0;
 424}
 425
 426static int v9fs_synth_statfs(FsContext *s, V9fsPath *fs_path,
 427                             struct statfs *stbuf)
 428{
 429    stbuf->f_type = 0xABCD;
 430    stbuf->f_bsize = 512;
 431    stbuf->f_blocks = 0;
 432    stbuf->f_files = v9fs_synth_node_count;
 433    stbuf->f_namelen = NAME_MAX;
 434    return 0;
 435}
 436
 437static ssize_t v9fs_synth_lgetxattr(FsContext *ctx, V9fsPath *path,
 438                                    const char *name, void *value, size_t size)
 439{
 440    errno = ENOTSUP;
 441    return -1;
 442}
 443
 444static ssize_t v9fs_synth_llistxattr(FsContext *ctx, V9fsPath *path,
 445                                     void *value, size_t size)
 446{
 447    errno = ENOTSUP;
 448    return -1;
 449}
 450
 451static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path,
 452                                const char *name, void *value,
 453                                size_t size, int flags)
 454{
 455    errno = ENOTSUP;
 456    return -1;
 457}
 458
 459static int v9fs_synth_lremovexattr(FsContext *ctx,
 460                                   V9fsPath *path, const char *name)
 461{
 462    errno = ENOTSUP;
 463    return -1;
 464}
 465
 466static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
 467                                   const char *name, V9fsPath *target)
 468{
 469    V9fsSynthNode *node;
 470    V9fsSynthNode *dir_node;
 471
 472    /* "." and ".." are not allowed */
 473    if (!strcmp(name, ".") || !strcmp(name, "..")) {
 474        errno = EINVAL;
 475        return -1;
 476
 477    }
 478    if (!dir_path) {
 479        dir_node = &v9fs_synth_root;
 480    } else {
 481        dir_node = *(V9fsSynthNode **)dir_path->data;
 482    }
 483    if (!strcmp(name, "/")) {
 484        node = dir_node;
 485        goto out;
 486    }
 487    /* search for the name in the childern */
 488    rcu_read_lock();
 489    QLIST_FOREACH(node, &dir_node->child, sibling) {
 490        if (!strcmp(node->name, name)) {
 491            break;
 492        }
 493    }
 494    rcu_read_unlock();
 495
 496    if (!node) {
 497        errno = ENOENT;
 498        return -1;
 499    }
 500out:
 501    /* Copy the node pointer to fid */
 502    target->data = g_malloc(sizeof(void *));
 503    memcpy(target->data, &node, sizeof(void *));
 504    target->size = sizeof(void *);
 505    return 0;
 506}
 507
 508static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir,
 509                               const char *old_name, V9fsPath *newdir,
 510                               const char *new_name)
 511{
 512    errno = EPERM;
 513    return -1;
 514}
 515
 516static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir,
 517                               const char *name, int flags)
 518{
 519    errno = EPERM;
 520    return -1;
 521}
 522
 523static int v9fs_synth_init(FsContext *ctx)
 524{
 525    QLIST_INIT(&v9fs_synth_root.child);
 526    qemu_mutex_init(&v9fs_synth_mutex);
 527
 528    /* Add "." and ".." entries for root */
 529    v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
 530                      "..", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
 531    v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
 532                      ".", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
 533
 534    /* Mark the subsystem is ready for use */
 535    v9fs_synth_fs = 1;
 536    return 0;
 537}
 538
 539FileOperations synth_ops = {
 540    .init         = v9fs_synth_init,
 541    .lstat        = v9fs_synth_lstat,
 542    .readlink     = v9fs_synth_readlink,
 543    .close        = v9fs_synth_close,
 544    .closedir     = v9fs_synth_closedir,
 545    .open         = v9fs_synth_open,
 546    .opendir      = v9fs_synth_opendir,
 547    .rewinddir    = v9fs_synth_rewinddir,
 548    .telldir      = v9fs_synth_telldir,
 549    .readdir_r    = v9fs_synth_readdir_r,
 550    .seekdir      = v9fs_synth_seekdir,
 551    .preadv       = v9fs_synth_preadv,
 552    .pwritev      = v9fs_synth_pwritev,
 553    .chmod        = v9fs_synth_chmod,
 554    .mknod        = v9fs_synth_mknod,
 555    .mkdir        = v9fs_synth_mkdir,
 556    .fstat        = v9fs_synth_fstat,
 557    .open2        = v9fs_synth_open2,
 558    .symlink      = v9fs_synth_symlink,
 559    .link         = v9fs_synth_link,
 560    .truncate     = v9fs_synth_truncate,
 561    .rename       = v9fs_synth_rename,
 562    .chown        = v9fs_synth_chown,
 563    .utimensat    = v9fs_synth_utimensat,
 564    .remove       = v9fs_synth_remove,
 565    .fsync        = v9fs_synth_fsync,
 566    .statfs       = v9fs_synth_statfs,
 567    .lgetxattr    = v9fs_synth_lgetxattr,
 568    .llistxattr   = v9fs_synth_llistxattr,
 569    .lsetxattr    = v9fs_synth_lsetxattr,
 570    .lremovexattr = v9fs_synth_lremovexattr,
 571    .name_to_path = v9fs_synth_name_to_path,
 572    .renameat     = v9fs_synth_renameat,
 573    .unlinkat     = v9fs_synth_unlinkat,
 574};
 575