qemu/hw/9pfs/virtio-9p-synth.c
Paolo Bonzini 7911747bd4 rcu: add rcu library
This includes a (mangled) copy of the liburcu code.  The main changes
are: 1) removing dependencies on many other header files in liburcu; 2)
removing for simplicity the tentative busy waiting in synchronize_rcu,
which has limited performance effects; 3) replacing futexes in
synchronize_rcu with QemuEvents for Win32 portability.  The API is
the same as liburcu, so it should be possible in the future to require
liburcu on POSIX systems for example and use our copy only on Windows.

Among the various versions available I chose urcu-mb, which is the
least invasive implementation even though it does not have the
fastest rcu_read_{lock,unlock} implementation.  The urcu flavor can
be changed later, after benchmarking.

Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2015-02-02 16:55:10 +01:00

574 lines
15 KiB
C

/*
* Virtio 9p synthetic file system support
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Malahal Naineni <malahal@us.ibm.com>
* Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#include "hw/virtio/virtio.h"
#include "virtio-9p.h"
#include "virtio-9p-xattr.h"
#include "fsdev/qemu-fsdev.h"
#include "virtio-9p-synth.h"
#include "qemu/rcu.h"
#include <sys/stat.h>
/* Root node for synth file system */
static V9fsSynthNode v9fs_synth_root = {
.name = "/",
.actual_attr = {
.mode = 0555 | S_IFDIR,
.nlink = 1,
},
.attr = &v9fs_synth_root.actual_attr,
};
static QemuMutex v9fs_synth_mutex;
static int v9fs_synth_node_count;
/* set to 1 when the synth fs is ready */
static int v9fs_synth_fs;
static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode,
const char *name,
V9fsSynthNodeAttr *attr, int inode)
{
V9fsSynthNode *node;
/* Add directory type and remove write bits */
mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH);
node = g_malloc0(sizeof(V9fsSynthNode));
if (attr) {
/* We are adding .. or . entries */
node->attr = attr;
node->attr->nlink++;
} else {
node->attr = &node->actual_attr;
node->attr->inode = inode;
node->attr->nlink = 1;
/* We don't allow write to directories */
node->attr->mode = mode;
node->attr->write = NULL;
node->attr->read = NULL;
}
node->private = node;
pstrcpy(node->name, sizeof(node->name), name);
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
return node;
}
int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
const char *name, V9fsSynthNode **result)
{
int ret;
V9fsSynthNode *node, *tmp;
if (!v9fs_synth_fs) {
return EAGAIN;
}
if (!name || (strlen(name) >= NAME_MAX)) {
return EINVAL;
}
if (!parent) {
parent = &v9fs_synth_root;
}
qemu_mutex_lock(&v9fs_synth_mutex);
QLIST_FOREACH(tmp, &parent->child, sibling) {
if (!strcmp(tmp->name, name)) {
ret = EEXIST;
goto err_out;
}
}
/* Add the name */
node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++);
v9fs_add_dir_node(node, parent->attr->mode, "..",
parent->attr, parent->attr->inode);
v9fs_add_dir_node(node, node->attr->mode, ".",
node->attr, node->attr->inode);
*result = node;
ret = 0;
err_out:
qemu_mutex_unlock(&v9fs_synth_mutex);
return ret;
}
int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
const char *name, v9fs_synth_read read,
v9fs_synth_write write, void *arg)
{
int ret;
V9fsSynthNode *node, *tmp;
if (!v9fs_synth_fs) {
return EAGAIN;
}
if (!name || (strlen(name) >= NAME_MAX)) {
return EINVAL;
}
if (!parent) {
parent = &v9fs_synth_root;
}
qemu_mutex_lock(&v9fs_synth_mutex);
QLIST_FOREACH(tmp, &parent->child, sibling) {
if (!strcmp(tmp->name, name)) {
ret = EEXIST;
goto err_out;
}
}
/* Add file type and remove write bits */
mode = ((mode & 0777) | S_IFREG);
node = g_malloc0(sizeof(V9fsSynthNode));
node->attr = &node->actual_attr;
node->attr->inode = v9fs_synth_node_count++;
node->attr->nlink = 1;
node->attr->read = read;
node->attr->write = write;
node->attr->mode = mode;
node->private = arg;
pstrcpy(node->name, sizeof(node->name), name);
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
ret = 0;
err_out:
qemu_mutex_unlock(&v9fs_synth_mutex);
return ret;
}
static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf)
{
stbuf->st_dev = 0;
stbuf->st_ino = node->attr->inode;
stbuf->st_mode = node->attr->mode;
stbuf->st_nlink = node->attr->nlink;
stbuf->st_uid = 0;
stbuf->st_gid = 0;
stbuf->st_rdev = 0;
stbuf->st_size = 0;
stbuf->st_blksize = 0;
stbuf->st_blocks = 0;
stbuf->st_atime = 0;
stbuf->st_mtime = 0;
stbuf->st_ctime = 0;
}
static int v9fs_synth_lstat(FsContext *fs_ctx,
V9fsPath *fs_path, struct stat *stbuf)
{
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
v9fs_synth_fill_statbuf(node, stbuf);
return 0;
}
static int v9fs_synth_fstat(FsContext *fs_ctx, int fid_type,
V9fsFidOpenState *fs, struct stat *stbuf)
{
V9fsSynthOpenState *synth_open = fs->private;
v9fs_synth_fill_statbuf(synth_open->node, stbuf);
return 0;
}
static int v9fs_synth_opendir(FsContext *ctx,
V9fsPath *fs_path, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open;
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
synth_open = g_malloc(sizeof(*synth_open));
synth_open->node = node;
node->open_count++;
fs->private = synth_open;
return 0;
}
static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open = fs->private;
V9fsSynthNode *node = synth_open->node;
node->open_count--;
g_free(synth_open);
fs->private = NULL;
return 0;
}
static off_t v9fs_synth_telldir(FsContext *ctx, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open = fs->private;
return synth_open->offset;
}
static void v9fs_synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
{
V9fsSynthOpenState *synth_open = fs->private;
synth_open->offset = off;
}
static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
{
v9fs_synth_seekdir(ctx, fs, 0);
}
static void v9fs_synth_direntry(V9fsSynthNode *node,
struct dirent *entry, off_t off)
{
strcpy(entry->d_name, node->name);
entry->d_ino = node->attr->inode;
entry->d_off = off + 1;
}
static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry,
struct dirent **result, off_t off)
{
int i = 0;
V9fsSynthNode *node;
rcu_read_lock();
QLIST_FOREACH(node, &dir->child, sibling) {
/* This is the off child of the directory */
if (i == off) {
break;
}
i++;
}
rcu_read_unlock();
if (!node) {
/* end of directory */
*result = NULL;
return 0;
}
v9fs_synth_direntry(node, entry, off);
*result = entry;
return 0;
}
static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
struct dirent *entry, struct dirent **result)
{
int ret;
V9fsSynthOpenState *synth_open = fs->private;
V9fsSynthNode *node = synth_open->node;
ret = v9fs_synth_get_dentry(node, entry, result, synth_open->offset);
if (!ret && *result != NULL) {
synth_open->offset++;
}
return ret;
}
static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path,
int flags, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open;
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
synth_open = g_malloc(sizeof(*synth_open));
synth_open->node = node;
node->open_count++;
fs->private = synth_open;
return 0;
}
static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path,
const char *name, int flags,
FsCred *credp, V9fsFidOpenState *fs)
{
errno = ENOSYS;
return -1;
}
static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open = fs->private;
V9fsSynthNode *node = synth_open->node;
node->open_count--;
g_free(synth_open);
fs->private = NULL;
return 0;
}
static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
const struct iovec *iov,
int iovcnt, off_t offset)
{
int i, count = 0, wcount;
V9fsSynthOpenState *synth_open = fs->private;
V9fsSynthNode *node = synth_open->node;
if (!node->attr->write) {
errno = EPERM;
return -1;
}
for (i = 0; i < iovcnt; i++) {
wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len,
offset, node->private);
offset += wcount;
count += wcount;
/* If we wrote less than requested. we are done */
if (wcount < iov[i].iov_len) {
break;
}
}
return count;
}
static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
const struct iovec *iov,
int iovcnt, off_t offset)
{
int i, count = 0, rcount;
V9fsSynthOpenState *synth_open = fs->private;
V9fsSynthNode *node = synth_open->node;
if (!node->attr->read) {
errno = EPERM;
return -1;
}
for (i = 0; i < iovcnt; i++) {
rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len,
offset, node->private);
offset += rcount;
count += rcount;
/* If we read less than requested. we are done */
if (rcount < iov[i].iov_len) {
break;
}
}
return count;
}
static int v9fs_synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
{
errno = ENOSYS;
return -1;
}
static int v9fs_synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
{
errno = EPERM;
return -1;
}
static int v9fs_synth_mknod(FsContext *fs_ctx, V9fsPath *path,
const char *buf, FsCred *credp)
{
errno = EPERM;
return -1;
}
static int v9fs_synth_mkdir(FsContext *fs_ctx, V9fsPath *path,
const char *buf, FsCred *credp)
{
errno = EPERM;
return -1;
}
static ssize_t v9fs_synth_readlink(FsContext *fs_ctx, V9fsPath *path,
char *buf, size_t bufsz)
{
errno = ENOSYS;
return -1;
}
static int v9fs_synth_symlink(FsContext *fs_ctx, const char *oldpath,
V9fsPath *newpath, const char *buf, FsCred *credp)
{
errno = EPERM;
return -1;
}
static int v9fs_synth_link(FsContext *fs_ctx, V9fsPath *oldpath,
V9fsPath *newpath, const char *buf)
{
errno = EPERM;
return -1;
}
static int v9fs_synth_rename(FsContext *ctx, const char *oldpath,
const char *newpath)
{
errno = EPERM;
return -1;
}
static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
{
errno = EPERM;
return -1;
}
static int v9fs_synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
const struct timespec *buf)
{
errno = EPERM;
return 0;
}
static int v9fs_synth_remove(FsContext *ctx, const char *path)
{
errno = EPERM;
return -1;
}
static int v9fs_synth_fsync(FsContext *ctx, int fid_type,
V9fsFidOpenState *fs, int datasync)
{
errno = ENOSYS;
return 0;
}
static int v9fs_synth_statfs(FsContext *s, V9fsPath *fs_path,
struct statfs *stbuf)
{
stbuf->f_type = 0xABCD;
stbuf->f_bsize = 512;
stbuf->f_blocks = 0;
stbuf->f_files = v9fs_synth_node_count;
stbuf->f_namelen = NAME_MAX;
return 0;
}
static ssize_t v9fs_synth_lgetxattr(FsContext *ctx, V9fsPath *path,
const char *name, void *value, size_t size)
{
errno = ENOTSUP;
return -1;
}
static ssize_t v9fs_synth_llistxattr(FsContext *ctx, V9fsPath *path,
void *value, size_t size)
{
errno = ENOTSUP;
return -1;
}
static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path,
const char *name, void *value,
size_t size, int flags)
{
errno = ENOTSUP;
return -1;
}
static int v9fs_synth_lremovexattr(FsContext *ctx,
V9fsPath *path, const char *name)
{
errno = ENOTSUP;
return -1;
}
static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
const char *name, V9fsPath *target)
{
V9fsSynthNode *node;
V9fsSynthNode *dir_node;
/* "." and ".." are not allowed */
if (!strcmp(name, ".") || !strcmp(name, "..")) {
errno = EINVAL;
return -1;
}
if (!dir_path) {
dir_node = &v9fs_synth_root;
} else {
dir_node = *(V9fsSynthNode **)dir_path->data;
}
if (!strcmp(name, "/")) {
node = dir_node;
goto out;
}
/* search for the name in the childern */
rcu_read_lock();
QLIST_FOREACH(node, &dir_node->child, sibling) {
if (!strcmp(node->name, name)) {
break;
}
}
rcu_read_unlock();
if (!node) {
errno = ENOENT;
return -1;
}
out:
/* Copy the node pointer to fid */
target->data = g_malloc(sizeof(void *));
memcpy(target->data, &node, sizeof(void *));
target->size = sizeof(void *);
return 0;
}
static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir,
const char *old_name, V9fsPath *newdir,
const char *new_name)
{
errno = EPERM;
return -1;
}
static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir,
const char *name, int flags)
{
errno = EPERM;
return -1;
}
static int v9fs_synth_init(FsContext *ctx)
{
QLIST_INIT(&v9fs_synth_root.child);
qemu_mutex_init(&v9fs_synth_mutex);
/* Add "." and ".." entries for root */
v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
"..", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
".", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
/* Mark the subsystem is ready for use */
v9fs_synth_fs = 1;
return 0;
}
FileOperations synth_ops = {
.init = v9fs_synth_init,
.lstat = v9fs_synth_lstat,
.readlink = v9fs_synth_readlink,
.close = v9fs_synth_close,
.closedir = v9fs_synth_closedir,
.open = v9fs_synth_open,
.opendir = v9fs_synth_opendir,
.rewinddir = v9fs_synth_rewinddir,
.telldir = v9fs_synth_telldir,
.readdir_r = v9fs_synth_readdir_r,
.seekdir = v9fs_synth_seekdir,
.preadv = v9fs_synth_preadv,
.pwritev = v9fs_synth_pwritev,
.chmod = v9fs_synth_chmod,
.mknod = v9fs_synth_mknod,
.mkdir = v9fs_synth_mkdir,
.fstat = v9fs_synth_fstat,
.open2 = v9fs_synth_open2,
.symlink = v9fs_synth_symlink,
.link = v9fs_synth_link,
.truncate = v9fs_synth_truncate,
.rename = v9fs_synth_rename,
.chown = v9fs_synth_chown,
.utimensat = v9fs_synth_utimensat,
.remove = v9fs_synth_remove,
.fsync = v9fs_synth_fsync,
.statfs = v9fs_synth_statfs,
.lgetxattr = v9fs_synth_lgetxattr,
.llistxattr = v9fs_synth_llistxattr,
.lsetxattr = v9fs_synth_lsetxattr,
.lremovexattr = v9fs_synth_lremovexattr,
.name_to_path = v9fs_synth_name_to_path,
.renameat = v9fs_synth_renameat,
.unlinkat = v9fs_synth_unlinkat,
};